flocc 0.5.18 → 0.5.21
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/dist/agents/Agent.d.ts +170 -33
- package/dist/environments/Environment.d.ts +133 -37
- package/dist/environments/GridEnvironment.d.ts +20 -6
- package/dist/flocc.es.js +1489 -488
- package/dist/flocc.js +1489 -488
- package/dist/helpers/KDTree.d.ts +2 -2
- package/dist/helpers/Network.d.ts +143 -67
- package/dist/helpers/NumArray.d.ts +1 -0
- package/dist/helpers/Rule.d.ts +58 -0
- package/dist/helpers/Terrain.d.ts +134 -27
- package/dist/helpers/Vector.d.ts +151 -22
- package/dist/renderers/ASCIIRenderer.d.ts +32 -3
- package/dist/renderers/AbstractRenderer.d.ts +17 -2
- package/dist/renderers/CanvasRenderer.d.ts +55 -0
- package/dist/renderers/Heatmap.d.ts +51 -6
- package/dist/renderers/TableRenderer.d.ts +94 -2
- package/dist/types/HeatmapAxis.d.ts +10 -0
- package/dist/utils/clamp.d.ts +9 -5
- package/dist/utils/distance.d.ts +15 -6
- package/dist/utils/gaussian.d.ts +9 -3
- package/dist/utils/gcd.d.ts +8 -0
- package/dist/utils/internal/copyArray.d.ts +1 -1
- package/dist/utils/lerp.d.ts +13 -4
- package/dist/utils/manhattanDistance.d.ts +15 -7
- package/dist/utils/max.d.ts +9 -0
- package/dist/utils/mean.d.ts +9 -3
- package/dist/utils/median.d.ts +10 -2
- package/dist/utils/min.d.ts +8 -0
- package/dist/utils/sample.d.ts +2 -0
- package/dist/version.d.ts +3 -0
- package/package.json +7 -3
- package/dist/helpers/Data.d.ts +0 -5
- package/dist/utils/internal/torusNormalize.d.ts +0 -1
package/dist/flocc.js
CHANGED
|
@@ -378,10 +378,19 @@
|
|
|
378
378
|
}
|
|
379
379
|
|
|
380
380
|
/**
|
|
381
|
-
* Linearly
|
|
382
|
-
*
|
|
383
|
-
*
|
|
384
|
-
*
|
|
381
|
+
* Linearly interpolates between `x` and `y`. The third parameter `t` (usually
|
|
382
|
+
* a value between `0` and `1`) is the amount by which to interpolate — a value of `0`
|
|
383
|
+
* returns the `x` value and `1` returns the `y` value.
|
|
384
|
+
*
|
|
385
|
+
* ```js
|
|
386
|
+
* lerp(5, 10, 0.5); // returns 7.5
|
|
387
|
+
* lerp(0, 100, 0.1); // returns 10
|
|
388
|
+
* lerp(22, 79, 1); // returns 79
|
|
389
|
+
* ```
|
|
390
|
+
*
|
|
391
|
+
* @param x The first value.
|
|
392
|
+
* @param y The second value.
|
|
393
|
+
* @param t The amount by which to interpolate (0 returns x, 1 returns y).
|
|
385
394
|
* @since 0.2.4
|
|
386
395
|
*/
|
|
387
396
|
function lerp(x, y, t) {
|
|
@@ -392,7 +401,7 @@
|
|
|
392
401
|
* Copies the values of `source` to `arr`
|
|
393
402
|
* or to a new Array.
|
|
394
403
|
*
|
|
395
|
-
* @
|
|
404
|
+
* @hidden
|
|
396
405
|
* @param {Array} source The Array to copy values from.
|
|
397
406
|
* @param {Array} [arr=[]] The Array to copy values to.
|
|
398
407
|
* @returns {Array}
|
|
@@ -409,6 +418,8 @@
|
|
|
409
418
|
}
|
|
410
419
|
|
|
411
420
|
/**
|
|
421
|
+
* A `Vector` contains multi-dimensional numeric data.
|
|
422
|
+
*
|
|
412
423
|
* @since 0.1.0
|
|
413
424
|
*/
|
|
414
425
|
var Vector = /** @class */ (function () {
|
|
@@ -421,20 +432,40 @@
|
|
|
421
432
|
this.dimension = data ? data.length : 0;
|
|
422
433
|
}
|
|
423
434
|
/**
|
|
435
|
+
* Retrieve a value from a `Vector` by its index. If the given index is greater than the
|
|
436
|
+
* `Vector`'s dimension, this returns `0` by default.
|
|
437
|
+
*
|
|
438
|
+
* ```js
|
|
439
|
+
* const v = new Vector(1, 2, 4);
|
|
440
|
+
*
|
|
441
|
+
* v.index(0); // returns 1
|
|
442
|
+
* v.index(2); // returns 4
|
|
443
|
+
* v.index(5); // returns 0
|
|
444
|
+
* ```
|
|
424
445
|
* @since 0.1.0
|
|
425
446
|
*/
|
|
426
|
-
Vector.prototype.index = function (
|
|
427
|
-
if (this.dimension >
|
|
428
|
-
return this.data[
|
|
447
|
+
Vector.prototype.index = function (i) {
|
|
448
|
+
if (this.dimension > i) {
|
|
449
|
+
return this.data[i];
|
|
429
450
|
}
|
|
430
451
|
// Attempting to access index ${n} on a vector greater than the vector's dimension returns 0 by default
|
|
431
452
|
return 0;
|
|
432
453
|
};
|
|
433
454
|
/**
|
|
434
|
-
*
|
|
435
|
-
* the dimension will be increased to the dimensionality implied by the index.
|
|
436
|
-
* @param i
|
|
437
|
-
* @param value
|
|
455
|
+
* Set the value at a given index. If the index is greater than the {@linkcode dimension}
|
|
456
|
+
* of this `Vector`, the dimension will be increased to the dimensionality implied by the index.
|
|
457
|
+
* @param i The numerical index (0-based) or lowercase string value (e.g. `"x"`) to set.
|
|
458
|
+
* @param value The value to set at this index/position.
|
|
459
|
+
*
|
|
460
|
+
* ```js
|
|
461
|
+
* const vector = new Vector();
|
|
462
|
+
* vector.set(0, 10);
|
|
463
|
+
* vector.set('y', 2);
|
|
464
|
+
* vector.set(2, 4);
|
|
465
|
+
*
|
|
466
|
+
* vector.xyz; // [10, 2, 4]
|
|
467
|
+
* ```
|
|
468
|
+
*
|
|
438
469
|
* @since 0.1.0
|
|
439
470
|
*/
|
|
440
471
|
Vector.prototype.set = function (i, value) {
|
|
@@ -462,128 +493,175 @@
|
|
|
462
493
|
return this;
|
|
463
494
|
};
|
|
464
495
|
Object.defineProperty(Vector.prototype, "x", {
|
|
496
|
+
/** @since 0.1.0 */
|
|
465
497
|
get: function () {
|
|
466
498
|
return this.index(0);
|
|
467
499
|
},
|
|
500
|
+
/** @since 0.1.0 */
|
|
468
501
|
set: function (n) {
|
|
469
502
|
this.set(0, n);
|
|
470
503
|
},
|
|
471
|
-
enumerable:
|
|
504
|
+
enumerable: false,
|
|
472
505
|
configurable: true
|
|
473
506
|
});
|
|
474
507
|
Object.defineProperty(Vector.prototype, "y", {
|
|
508
|
+
/** @since 0.1.0 */
|
|
475
509
|
get: function () {
|
|
476
510
|
return this.index(1);
|
|
477
511
|
},
|
|
512
|
+
/** @since 0.1.0 */
|
|
478
513
|
set: function (n) {
|
|
479
514
|
this.set(1, n);
|
|
480
515
|
},
|
|
481
|
-
enumerable:
|
|
516
|
+
enumerable: false,
|
|
482
517
|
configurable: true
|
|
483
518
|
});
|
|
484
519
|
Object.defineProperty(Vector.prototype, "z", {
|
|
520
|
+
/** @since 0.1.0 */
|
|
485
521
|
get: function () {
|
|
486
522
|
return this.index(2);
|
|
487
523
|
},
|
|
524
|
+
/** @since 0.1.0 */
|
|
488
525
|
set: function (n) {
|
|
489
526
|
this.set(2, n);
|
|
490
527
|
},
|
|
491
|
-
enumerable:
|
|
528
|
+
enumerable: false,
|
|
492
529
|
configurable: true
|
|
493
530
|
});
|
|
494
531
|
Object.defineProperty(Vector.prototype, "w", {
|
|
532
|
+
/** @since 0.1.0 */
|
|
495
533
|
get: function () {
|
|
496
534
|
return this.index(3);
|
|
497
535
|
},
|
|
536
|
+
/** @since 0.1.0 */
|
|
498
537
|
set: function (n) {
|
|
499
538
|
this.set(3, n);
|
|
500
539
|
},
|
|
501
|
-
enumerable:
|
|
540
|
+
enumerable: false,
|
|
502
541
|
configurable: true
|
|
503
542
|
});
|
|
504
543
|
Object.defineProperty(Vector.prototype, "xy", {
|
|
544
|
+
/** @since 0.2.4 */
|
|
505
545
|
get: function () {
|
|
506
546
|
return [this.index(0), this.index(1)];
|
|
507
547
|
},
|
|
508
|
-
enumerable:
|
|
548
|
+
enumerable: false,
|
|
509
549
|
configurable: true
|
|
510
550
|
});
|
|
511
551
|
Object.defineProperty(Vector.prototype, "xz", {
|
|
552
|
+
/** @since 0.2.4 */
|
|
512
553
|
get: function () {
|
|
513
554
|
return [this.index(0), this.index(2)];
|
|
514
555
|
},
|
|
515
|
-
enumerable:
|
|
556
|
+
enumerable: false,
|
|
516
557
|
configurable: true
|
|
517
558
|
});
|
|
518
559
|
Object.defineProperty(Vector.prototype, "yz", {
|
|
560
|
+
/** @since 0.2.4 */
|
|
519
561
|
get: function () {
|
|
520
562
|
return [this.index(1), this.index(2)];
|
|
521
563
|
},
|
|
522
|
-
enumerable:
|
|
564
|
+
enumerable: false,
|
|
523
565
|
configurable: true
|
|
524
566
|
});
|
|
525
567
|
Object.defineProperty(Vector.prototype, "xyz", {
|
|
568
|
+
/** @since 0.2.4 */
|
|
526
569
|
get: function () {
|
|
527
570
|
return [this.index(0), this.index(1), this.index(2)];
|
|
528
571
|
},
|
|
529
|
-
enumerable:
|
|
572
|
+
enumerable: false,
|
|
530
573
|
configurable: true
|
|
531
574
|
});
|
|
532
575
|
Object.defineProperty(Vector.prototype, "r", {
|
|
576
|
+
/**
|
|
577
|
+
* `r` for 'red' (the 1st value)
|
|
578
|
+
* @since 0.1.0
|
|
579
|
+
*/
|
|
533
580
|
get: function () {
|
|
534
581
|
return this.index(0);
|
|
535
582
|
},
|
|
583
|
+
/**
|
|
584
|
+
* `r` for 'red' (the 1st value)
|
|
585
|
+
* @since 0.1.0
|
|
586
|
+
*/
|
|
536
587
|
set: function (n) {
|
|
537
588
|
this.set(0, n);
|
|
538
589
|
},
|
|
539
|
-
enumerable:
|
|
590
|
+
enumerable: false,
|
|
540
591
|
configurable: true
|
|
541
592
|
});
|
|
542
593
|
Object.defineProperty(Vector.prototype, "g", {
|
|
594
|
+
/**
|
|
595
|
+
* `g` for 'green' (the 2nd value)
|
|
596
|
+
* @since 0.1.0
|
|
597
|
+
*/
|
|
543
598
|
get: function () {
|
|
544
599
|
return this.index(1);
|
|
545
600
|
},
|
|
601
|
+
/**
|
|
602
|
+
* `g` for 'green' (the 2nd value)
|
|
603
|
+
* @since 0.1.0
|
|
604
|
+
*/
|
|
546
605
|
set: function (n) {
|
|
547
606
|
this.set(1, n);
|
|
548
607
|
},
|
|
549
|
-
enumerable:
|
|
608
|
+
enumerable: false,
|
|
550
609
|
configurable: true
|
|
551
610
|
});
|
|
552
611
|
Object.defineProperty(Vector.prototype, "b", {
|
|
612
|
+
/**
|
|
613
|
+
* `b` for 'blue' (the 3rd value)
|
|
614
|
+
* @since 0.1.0
|
|
615
|
+
*/
|
|
553
616
|
get: function () {
|
|
554
617
|
return this.index(2);
|
|
555
618
|
},
|
|
619
|
+
/**
|
|
620
|
+
* `b` for 'blue' (the 3rd value)
|
|
621
|
+
* @since 0.1.0
|
|
622
|
+
*/
|
|
556
623
|
set: function (n) {
|
|
557
624
|
this.set(2, n);
|
|
558
625
|
},
|
|
559
|
-
enumerable:
|
|
626
|
+
enumerable: false,
|
|
560
627
|
configurable: true
|
|
561
628
|
});
|
|
562
629
|
Object.defineProperty(Vector.prototype, "a", {
|
|
630
|
+
/**
|
|
631
|
+
* `a` for 'alpha' (the 4th value)
|
|
632
|
+
* @since 0.1.0
|
|
633
|
+
*/
|
|
563
634
|
get: function () {
|
|
564
635
|
return this.index(3);
|
|
565
636
|
},
|
|
637
|
+
/**
|
|
638
|
+
* `a` for 'alpha' (the 4th value)
|
|
639
|
+
* @since 0.1.0
|
|
640
|
+
*/
|
|
566
641
|
set: function (n) {
|
|
567
642
|
this.set(3, n);
|
|
568
643
|
},
|
|
569
|
-
enumerable:
|
|
644
|
+
enumerable: false,
|
|
570
645
|
configurable: true
|
|
571
646
|
});
|
|
572
647
|
Object.defineProperty(Vector.prototype, "rgb", {
|
|
648
|
+
/** @since 0.2.4 */
|
|
573
649
|
get: function () {
|
|
574
650
|
return [this.index(0), this.index(1), this.index(2)];
|
|
575
651
|
},
|
|
576
|
-
enumerable:
|
|
652
|
+
enumerable: false,
|
|
577
653
|
configurable: true
|
|
578
654
|
});
|
|
579
655
|
Object.defineProperty(Vector.prototype, "rgba", {
|
|
656
|
+
/** @since 0.2.4 */
|
|
580
657
|
get: function () {
|
|
581
658
|
return [this.index(0), this.index(1), this.index(2), this.index(3)];
|
|
582
659
|
},
|
|
583
|
-
enumerable:
|
|
660
|
+
enumerable: false,
|
|
584
661
|
configurable: true
|
|
585
662
|
});
|
|
586
663
|
/**
|
|
664
|
+
* Add another `Vector` to this `Vector`. This *does* mutate the `Vector` that calls this method.
|
|
587
665
|
* @since 0.1.0
|
|
588
666
|
*/
|
|
589
667
|
Vector.prototype.add = function (v) {
|
|
@@ -598,6 +676,17 @@
|
|
|
598
676
|
return this;
|
|
599
677
|
};
|
|
600
678
|
/**
|
|
679
|
+
* Multiply this `Vector` by a scalar number. This *does* mutate the `Vector` that calls this method.
|
|
680
|
+
*
|
|
681
|
+
* ```js
|
|
682
|
+
* const v = new Vector(1, 2);
|
|
683
|
+
* v.multiplyScalar(5);
|
|
684
|
+
* v.xy; // returns [5, 10]
|
|
685
|
+
*
|
|
686
|
+
* v.multiplyScalar(-0.5);
|
|
687
|
+
* v.xy; // returns [-2.5, -5]
|
|
688
|
+
* ```
|
|
689
|
+
*
|
|
601
690
|
* @since 0.1.0
|
|
602
691
|
*/
|
|
603
692
|
Vector.prototype.multiplyScalar = function (n) {
|
|
@@ -605,6 +694,7 @@
|
|
|
605
694
|
return this;
|
|
606
695
|
};
|
|
607
696
|
/**
|
|
697
|
+
* Add a scalar number to all of this `Vector`'s values'. This *does* mutate the `Vector` that calls this method.
|
|
608
698
|
* @since 0.1.0
|
|
609
699
|
*/
|
|
610
700
|
Vector.prototype.addScalar = function (n) {
|
|
@@ -619,8 +709,14 @@
|
|
|
619
709
|
return Math.sqrt(sum(this.data.map(function (x) { return Math.pow(x, 2); })));
|
|
620
710
|
};
|
|
621
711
|
/**
|
|
622
|
-
* Normalize the
|
|
623
|
-
*
|
|
712
|
+
* Normalize the `Vector` (turn it into a `Vector` with length = `1`). Has no effect on the 0 `Vector`. This *does* mutate the `Vector` that calls this method.
|
|
713
|
+
*
|
|
714
|
+
* ```js
|
|
715
|
+
* const v = new Vector(5, 3, -1);
|
|
716
|
+
* v.normalize();
|
|
717
|
+
* v.length(); // returns 1
|
|
718
|
+
* ```
|
|
719
|
+
*
|
|
624
720
|
* @since 0.1.0
|
|
625
721
|
*/
|
|
626
722
|
Vector.prototype.normalize = function () {
|
|
@@ -631,6 +727,7 @@
|
|
|
631
727
|
return this;
|
|
632
728
|
};
|
|
633
729
|
/**
|
|
730
|
+
* Create a copy of this `Vector`.
|
|
634
731
|
* @since 0.1.0
|
|
635
732
|
*/
|
|
636
733
|
Vector.prototype.clone = function () {
|
|
@@ -638,8 +735,15 @@
|
|
|
638
735
|
return new (Vector.bind.apply(Vector, __spreadArrays([void 0], data)))();
|
|
639
736
|
};
|
|
640
737
|
/**
|
|
641
|
-
* Rotate
|
|
642
|
-
*
|
|
738
|
+
* Rotate the `Vector` about the z-axis by `angle` radians (updating its `x` and `y` values). This *does* mutate the `Vector` that calls this method.
|
|
739
|
+
*
|
|
740
|
+
* ```js
|
|
741
|
+
* const v = new Vector(1, 0);
|
|
742
|
+
* v.rotateZ(Math.PI / 2); // rotate by PI / 2 radians = 90 degrees
|
|
743
|
+
*
|
|
744
|
+
* v.xy; // returns [0, 1]
|
|
745
|
+
* ```
|
|
746
|
+
*
|
|
643
747
|
* @since 0.2.2
|
|
644
748
|
*/
|
|
645
749
|
Vector.prototype.rotateZ = function (angle) {
|
|
@@ -652,8 +756,8 @@
|
|
|
652
756
|
return this;
|
|
653
757
|
};
|
|
654
758
|
/**
|
|
655
|
-
* Get the dot product of this
|
|
656
|
-
* @
|
|
759
|
+
* Get the {@link https://en.wikipedia.org/wiki/Dot_product | dot product} of this `Vector` with another.
|
|
760
|
+
* @since 0.2.4
|
|
657
761
|
*/
|
|
658
762
|
Vector.prototype.dot = function (v) {
|
|
659
763
|
var dimension = Math.max(this.dimension, v.dimension);
|
|
@@ -663,11 +767,23 @@
|
|
|
663
767
|
return sum;
|
|
664
768
|
};
|
|
665
769
|
/**
|
|
666
|
-
* Linearly interpolate between this
|
|
667
|
-
*
|
|
668
|
-
*
|
|
669
|
-
*
|
|
770
|
+
* Linearly interpolate between this `Vector` and another `Vector`. This *does not* mutate the original `Vector` that calls this method, but returns a new `Vector`.
|
|
771
|
+
*
|
|
772
|
+
* ```js
|
|
773
|
+
* const a = new Vector(1, 3, -5);
|
|
774
|
+
* const b = new Vector(4, -2);
|
|
775
|
+
*
|
|
776
|
+
* a.lerp(b, 0); // returns a clone of Vector a
|
|
777
|
+
* a.lerp(b, 1); // returns a clone of Vector b
|
|
778
|
+
*
|
|
779
|
+
* const mid = a.lerp(b, 0.5); // returns a Vector halfway between a and b
|
|
780
|
+
* mid.xyz; // returns [2.5, 0.5, -2.5]
|
|
781
|
+
* ```
|
|
782
|
+
*
|
|
783
|
+
* @param v - The other vector.
|
|
784
|
+
* @param t - The amount by which to interpolate (usually between `0` and `1`, although it can be any number).
|
|
670
785
|
* @returns {Vector} - The new, interpolated vector.
|
|
786
|
+
* @since 0.2.4
|
|
671
787
|
*/
|
|
672
788
|
Vector.prototype.lerp = function (v, t) {
|
|
673
789
|
var longerVector = this.dimension > v.dimension ? this : v;
|
|
@@ -732,16 +848,70 @@
|
|
|
732
848
|
return obj[name].apply(obj, args);
|
|
733
849
|
};
|
|
734
850
|
/**
|
|
851
|
+
* The `Rule` class is an experimental interface for adding behavior to {@linkcode Agent}s. A `Rule` object may be used in place of a `tick` function to be added as `Agent` behavior using `agent.set('tick', tickRule)`. As a trivial example, consider the following `Rule`, which increments the `Agent`'s `x` value with every time step:
|
|
852
|
+
*
|
|
853
|
+
* ```js
|
|
854
|
+
* const rule = new Rule(environment, [
|
|
855
|
+
* "set", "x", [
|
|
856
|
+
* "add", 1, [
|
|
857
|
+
* "get", "x"
|
|
858
|
+
* ]
|
|
859
|
+
* ]
|
|
860
|
+
* ]);
|
|
861
|
+
* agent.set("tick", rule);
|
|
862
|
+
* ```
|
|
863
|
+
*
|
|
864
|
+
* Reading from the outer arrays inward, the steps of this `Rule` instructs the `Agent` to:
|
|
865
|
+
* - `set` the `Agent`'s `"x"` value to...
|
|
866
|
+
* - The result of `add`ing `1` and...
|
|
867
|
+
* - The `Agent`'s current `"x"` value
|
|
868
|
+
*
|
|
869
|
+
* Generally, `Rule` steps are a deeply nested array, where the first value of any given array is always an instruction or operator (e.g. `"set"`, `"add"`, `"filter"`). See the {@linkcode constructor} function for more information about steps.
|
|
735
870
|
* @since 0.3.0
|
|
736
871
|
*/
|
|
737
872
|
var Rule = /** @class */ (function () {
|
|
873
|
+
/**
|
|
874
|
+
* A single step may be as simple as `["get", "x"]`. This returns the `Agent`'s `"x"` value to the outer step that contains it. So, for example, the step `["add", 1, ["get", "x"]]`, working from the inside out, retrieves the `"x"` value and then adds `1` to it. More complex steps function similarly, always traversing to the deepest nested step, evaluating it, and 'unwrapping' until all steps have been executed.
|
|
875
|
+
*
|
|
876
|
+
* A step's first element should be a string that is one of the allowed operators, followed by a certain number of arguments.
|
|
877
|
+
*
|
|
878
|
+
* |Operator|Arguments|Notes|
|
|
879
|
+
* |---|---|---|
|
|
880
|
+
* |`"add"`|`2`|Pass 2 numbers, or two steps that evaluate to numbers|
|
|
881
|
+
* |`"subtract"`|`2`|""|
|
|
882
|
+
* |`"multiply"`|`2`|""|
|
|
883
|
+
* |`"divide"`|`2`|""|
|
|
884
|
+
* |`"mod"`|`2`|""|
|
|
885
|
+
* |`"power"`|`2`|""|
|
|
886
|
+
* |`"get"`|`1`|Pass the key of `Agent` data to retrieve|
|
|
887
|
+
* |`"set"`|`2`|Pass the key and value to set|
|
|
888
|
+
* |`"enqueue"`|`2`|Pass the key and value to enqueue|
|
|
889
|
+
* |`"local"`|`2`|Pass the key and value to set as local variables|
|
|
890
|
+
* |`"if"`|`3`|Pass the conditional (usually a step that evaluates to a boolean), the step to run when `true`, and the step to run when `false|
|
|
891
|
+
* |`"and"`|`2`|Pass the two steps to logically evaluate|
|
|
892
|
+
* |`"or"`|`2`|""|
|
|
893
|
+
* |`"gt"`|`2`|""|
|
|
894
|
+
* |`"gte"`|`2`|""|
|
|
895
|
+
* |`"lt"`|`2`|""|
|
|
896
|
+
* |`"lte"`|`2`|""|
|
|
897
|
+
* |`"eq"`|`2`|""|
|
|
898
|
+
* |`"map"`|`2`|Pass an array (or step that evaluates to an array) and a lambda to invoke for each element|
|
|
899
|
+
* |`"filter"`|`2`|""|
|
|
900
|
+
* |`"key"`|`2`|Pass an object (or step that evaluates to an object) and the key to retrieve from that object|
|
|
901
|
+
* |`"agent"`|`0`|No arguments; returns the `Agent`|
|
|
902
|
+
* |`"environment"`|`0`|No arguments, returns the `Environment`|
|
|
903
|
+
* |`"vector"`|`any`|Creates an n-dimensional {@linkcode Vector} from the supplied arguments|
|
|
904
|
+
*/
|
|
738
905
|
function Rule(environment, steps) {
|
|
739
906
|
var _this = this;
|
|
907
|
+
/** @hidden */
|
|
740
908
|
this.steps = [];
|
|
909
|
+
/** @hidden */
|
|
741
910
|
this.locals = {};
|
|
742
911
|
/**
|
|
743
912
|
* interpret single array step
|
|
744
913
|
* @since 0.3.0
|
|
914
|
+
* @hidden
|
|
745
915
|
*/
|
|
746
916
|
this.evaluate = function (agent, step) {
|
|
747
917
|
var first = step && step.length > 0 ? step[0] : null;
|
|
@@ -866,6 +1036,7 @@
|
|
|
866
1036
|
}
|
|
867
1037
|
/**
|
|
868
1038
|
* @since 0.3.0
|
|
1039
|
+
* @hidden
|
|
869
1040
|
*/
|
|
870
1041
|
Rule.prototype.call = function (agent) {
|
|
871
1042
|
return this.evaluate(agent, this.steps);
|
|
@@ -921,40 +1092,85 @@
|
|
|
921
1092
|
var warnOnce1 = once(console.warn.bind(console));
|
|
922
1093
|
var warnOnce2 = once(console.warn.bind(console));
|
|
923
1094
|
/**
|
|
1095
|
+
* This class puts the `Agent` in 'agent-based modeling.' More specifically,
|
|
1096
|
+
* an `Agent` represents an individual unit of data and its associated
|
|
1097
|
+
* behaviors.
|
|
924
1098
|
* @since 0.0.5
|
|
925
1099
|
*/
|
|
926
1100
|
var Agent = /** @class */ (function () {
|
|
1101
|
+
/**
|
|
1102
|
+
* `Agent`s can be instantiated with or without data. Instantiating
|
|
1103
|
+
* with data is equivalent to creating an `Agent` and immediately
|
|
1104
|
+
* calling {@linkcode Agent.set} to add data.
|
|
1105
|
+
*
|
|
1106
|
+
* ```js
|
|
1107
|
+
* // instantiates an Agent without data
|
|
1108
|
+
* const a = new Agent();
|
|
1109
|
+
*
|
|
1110
|
+
* // instantiates an Agent with data
|
|
1111
|
+
* const b = new Agent({
|
|
1112
|
+
* x: 50,
|
|
1113
|
+
* y: 100
|
|
1114
|
+
* });
|
|
1115
|
+
* ```
|
|
1116
|
+
* @param data
|
|
1117
|
+
*/
|
|
927
1118
|
function Agent(data) {
|
|
928
1119
|
if (data === void 0) { data = {}; }
|
|
929
1120
|
/**
|
|
930
|
-
* @
|
|
931
|
-
*
|
|
932
|
-
*
|
|
933
|
-
*
|
|
1121
|
+
* An `Agent` can only belong to a single {@linkcode Environment}. When
|
|
1122
|
+
* `environment.addAgent(agent);` is called, this is value is updated
|
|
1123
|
+
* to point to that `Environment`.
|
|
1124
|
+
*
|
|
1125
|
+
* ```js
|
|
1126
|
+
* const environment = new Environment();
|
|
1127
|
+
* const agent = new Agent();
|
|
1128
|
+
* agent.environment; // returns `null`
|
|
1129
|
+
*
|
|
1130
|
+
* environment.addAgent(agent);
|
|
1131
|
+
* agent.environment === environment; // returns `true`
|
|
934
1132
|
*/
|
|
935
1133
|
this.environment = null;
|
|
1134
|
+
/** @hidden */
|
|
936
1135
|
this.rules = [];
|
|
1136
|
+
/** @hidden */
|
|
937
1137
|
this.queue = [];
|
|
1138
|
+
/** @hidden */
|
|
938
1139
|
this.data = {};
|
|
1140
|
+
/**
|
|
1141
|
+
* `Agent`s are automatically assigned a unique ID when they are created.
|
|
1142
|
+
* This can be useful when you need to refer to a specific `Agent`, and
|
|
1143
|
+
* they can be retrieved using their ID from their `Environment` by calling
|
|
1144
|
+
* {@link Environment.getAgentById | `environment.getAgentById(id);`}
|
|
1145
|
+
* ```js
|
|
1146
|
+
* const agent = new Agent();
|
|
1147
|
+
* const id = agent.id; // returns "59B4F928-46C8-..." (for example)
|
|
1148
|
+
* ```
|
|
1149
|
+
*/
|
|
939
1150
|
this.id = uuid$1();
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
1151
|
+
/**
|
|
1152
|
+
* This is used as a temporary store for data that
|
|
1153
|
+
* gets returned from rules. When enqueued rules are executed,
|
|
1154
|
+
* even if there aren't any enqueued rules, .set gets called
|
|
1155
|
+
* on any data that was placed here.
|
|
1156
|
+
* @hidden
|
|
1157
|
+
*/
|
|
944
1158
|
this.__newData = {};
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
1159
|
+
/** When agent.get('key') is called, this pseudo-private member is set to 'key'.
|
|
1160
|
+
* Once it is retrieved, it is reset to null. If agent.get('key') is called before
|
|
1161
|
+
* this has been reset, that means that there is an infinite loop, and the call
|
|
1162
|
+
* will throw an error.
|
|
1163
|
+
* @hidden
|
|
1164
|
+
*/
|
|
949
1165
|
this.__retrievingData = null;
|
|
1166
|
+
/** @hidden */
|
|
950
1167
|
this.__subtree = null;
|
|
951
1168
|
this.set(data);
|
|
952
1169
|
}
|
|
953
1170
|
/**
|
|
954
1171
|
* Set a function value. `tick` and `queue` are not automatically called,
|
|
955
1172
|
* but any other named value will automatically be called when referenced.
|
|
956
|
-
* @
|
|
957
|
-
* @param {Function} fn
|
|
1173
|
+
* @hidden
|
|
958
1174
|
*/
|
|
959
1175
|
Agent.prototype._setFunctionValue = function (name, fn) {
|
|
960
1176
|
var _this = this;
|
|
@@ -970,9 +1186,8 @@
|
|
|
970
1186
|
}
|
|
971
1187
|
};
|
|
972
1188
|
/**
|
|
973
|
-
* Retrieve an arbitrary piece of data associated
|
|
974
|
-
*
|
|
975
|
-
* @param {string} name
|
|
1189
|
+
* Retrieve an arbitrary piece of data associated by name.
|
|
1190
|
+
* If the data has not been {@linkcode set}, returns `null`.
|
|
976
1191
|
* @since 0.0.5
|
|
977
1192
|
*/
|
|
978
1193
|
Agent.prototype.get = function (name) {
|
|
@@ -990,8 +1205,20 @@
|
|
|
990
1205
|
return data;
|
|
991
1206
|
};
|
|
992
1207
|
/**
|
|
993
|
-
* Retrieve all the data associated with this
|
|
994
|
-
*
|
|
1208
|
+
* Retrieve all the data associated with this `Agent` at once.
|
|
1209
|
+
*
|
|
1210
|
+
* ```js
|
|
1211
|
+
* agent.set('x', 3);
|
|
1212
|
+
* agent.set('color', 'blue');
|
|
1213
|
+
* agent.set('active', false);
|
|
1214
|
+
*
|
|
1215
|
+
* agent.getData();
|
|
1216
|
+
* // returns {
|
|
1217
|
+
* // x: 3,
|
|
1218
|
+
* // color: 'blue',
|
|
1219
|
+
* // active: false
|
|
1220
|
+
* // }
|
|
1221
|
+
* ```
|
|
995
1222
|
* @since 0.1.0
|
|
996
1223
|
*/
|
|
997
1224
|
Agent.prototype.getData = function () {
|
|
@@ -1025,6 +1252,7 @@
|
|
|
1025
1252
|
/**
|
|
1026
1253
|
* Helper function to set key-value pair depending on whether value
|
|
1027
1254
|
* is a function (callable) or not
|
|
1255
|
+
* @hidden
|
|
1028
1256
|
*/
|
|
1029
1257
|
Agent.prototype._setKeyValue = function (key, value) {
|
|
1030
1258
|
if (typeof value === "function") {
|
|
@@ -1063,25 +1291,57 @@
|
|
|
1063
1291
|
}
|
|
1064
1292
|
};
|
|
1065
1293
|
/**
|
|
1066
|
-
*
|
|
1067
|
-
*
|
|
1068
|
-
*
|
|
1069
|
-
*
|
|
1070
|
-
*
|
|
1294
|
+
* increment a numeric piece of data associated with this `Agent`
|
|
1295
|
+
* (increasing its value by 1). This method is *synchronous* —
|
|
1296
|
+
* it immediately increases the value (to *asynchronously* increase it,
|
|
1297
|
+
* the rule function should instead return a new value.
|
|
1298
|
+
*
|
|
1299
|
+
* ```js
|
|
1300
|
+
* agent.set('x', 50);
|
|
1301
|
+
* agent.increment('x');
|
|
1302
|
+
* agent.get('x'); // returns 51
|
|
1303
|
+
* ```
|
|
1304
|
+
*
|
|
1305
|
+
* If the second parameter `n` is included, decrements by that amount.
|
|
1306
|
+
*
|
|
1307
|
+
* ```js
|
|
1308
|
+
* agent.set('x', 50);
|
|
1309
|
+
* agent.increment('x', 10);
|
|
1310
|
+
* agent.get('x'); // returns 60
|
|
1311
|
+
* ```
|
|
1312
|
+
*
|
|
1313
|
+
* If the value has not yet been set, calling this method sets it to `1`
|
|
1314
|
+
* (or to `n`).
|
|
1071
1315
|
* @since 0.0.8
|
|
1072
1316
|
*/
|
|
1073
1317
|
Agent.prototype.increment = function (name, n) {
|
|
1074
1318
|
if (n === void 0) { n = 1; }
|
|
1075
|
-
if (
|
|
1319
|
+
if (this.get(name) === null)
|
|
1076
1320
|
this.set(name, 0);
|
|
1077
1321
|
this.set(name, this.get(name) + n);
|
|
1078
1322
|
};
|
|
1079
1323
|
/**
|
|
1080
|
-
* Decrement a numeric
|
|
1081
|
-
*
|
|
1082
|
-
*
|
|
1083
|
-
*
|
|
1084
|
-
*
|
|
1324
|
+
* Decrement a numeric piece of data associated with this `Agent`
|
|
1325
|
+
* (decreasing its value by 1). This method is *synchronous* —
|
|
1326
|
+
* it immediately decreases the value (to *asynchronously* decrease it,
|
|
1327
|
+
* the rule function should instead return a new value.
|
|
1328
|
+
*
|
|
1329
|
+
* ```js
|
|
1330
|
+
* agent.set('x', 50);
|
|
1331
|
+
* agent.decrement('x');
|
|
1332
|
+
* agent.get('x'); // returns 49
|
|
1333
|
+
* ```
|
|
1334
|
+
*
|
|
1335
|
+
* If the second parameter `n` is included, decrements by that amount.
|
|
1336
|
+
*
|
|
1337
|
+
* ```js
|
|
1338
|
+
* agent.set('x', 50);
|
|
1339
|
+
* agent.decrement('x', 10);
|
|
1340
|
+
* agent.get('x'); // returns 40
|
|
1341
|
+
* ```
|
|
1342
|
+
*
|
|
1343
|
+
* If the value has not yet been set, calling this method sets it to `-1`
|
|
1344
|
+
* (or to `-n`).
|
|
1085
1345
|
* @since 0.0.8
|
|
1086
1346
|
*/
|
|
1087
1347
|
Agent.prototype.decrement = function (name, n) {
|
|
@@ -1089,9 +1349,29 @@
|
|
|
1089
1349
|
this.increment(name, -n);
|
|
1090
1350
|
};
|
|
1091
1351
|
/**
|
|
1092
|
-
*
|
|
1093
|
-
*
|
|
1094
|
-
*
|
|
1352
|
+
* Until v0.5.14, this was the preferred way to add behavior to `Agent`s.
|
|
1353
|
+
* Now, the preferred method is by setting the `Agent`'s `"tick"` value (i.e. `agent.set({ tick: function(agt) { ... }})`).
|
|
1354
|
+
* This method will still be allowed until v0.7.0.
|
|
1355
|
+
*
|
|
1356
|
+
* Adds a rule (a function taking an `Agent` as a callback or a {@linkcode Rule} object) that may be run with every {@linkcode Environment.tick}.
|
|
1357
|
+
* It is possible to add *more than one rule* to an `Agent`, although it
|
|
1358
|
+
* is generally easier to write a longer function or to break it apart
|
|
1359
|
+
* into multiple functions.
|
|
1360
|
+
*
|
|
1361
|
+
* ```js
|
|
1362
|
+
* // adds a rule that *synchronously* increments the Agent's "x" value
|
|
1363
|
+
* agent.addRule(function(agt) {
|
|
1364
|
+
* agent.increment('x');
|
|
1365
|
+
* });
|
|
1366
|
+
*
|
|
1367
|
+
* // adds a rule that *asynchronously* increments the Agent's "x" value
|
|
1368
|
+
* agent.addRule(function(agt) {
|
|
1369
|
+
* return {
|
|
1370
|
+
* x: agt.get('x') + 1
|
|
1371
|
+
* };
|
|
1372
|
+
* });
|
|
1373
|
+
* ```
|
|
1374
|
+
*
|
|
1095
1375
|
* @deprecated since version 0.5.14
|
|
1096
1376
|
* @since 0.0.5
|
|
1097
1377
|
*/
|
|
@@ -1107,16 +1387,34 @@
|
|
|
1107
1387
|
});
|
|
1108
1388
|
};
|
|
1109
1389
|
/**
|
|
1110
|
-
*
|
|
1111
|
-
*
|
|
1112
|
-
*
|
|
1113
|
-
*
|
|
1114
|
-
*
|
|
1390
|
+
* Like {@linkcode Agent.addRule}, this method is deprecated and the
|
|
1391
|
+
* recommended way is to now call
|
|
1392
|
+
* `agent.set('queue', function(agt) { ... });`
|
|
1393
|
+
*
|
|
1394
|
+
* Calling this method enqueues a function to be executed
|
|
1395
|
+
* *asynchronously* (at the end of the {@linkcode Environment}'s tick cycle).
|
|
1396
|
+
* This is useful if a 'cleanup pass' should be performed between
|
|
1397
|
+
* time steps to adjust `Agent` data.
|
|
1398
|
+
*
|
|
1399
|
+
* Below, the `Agent` sets its `"x"` value to `30` whenever it is
|
|
1400
|
+
* activated during the `Environment`'s tick cycle. After all of that
|
|
1401
|
+
* cycle's `Agent`s have been activated, this `Agent` sets its `"x"`
|
|
1402
|
+
* value to `20`. So if any other `Agent` references its `"x"` value
|
|
1403
|
+
* during a tick cycle after it has been activated, it will return `30`,
|
|
1404
|
+
* but in between tick cycles it will return `20`.
|
|
1405
|
+
*
|
|
1406
|
+
* ```js
|
|
1407
|
+
* agent.addRule(agt => {
|
|
1408
|
+
* agt.set("x", 30);
|
|
1409
|
+
* agt.enqueue(a => {
|
|
1410
|
+
* a.set("x", 20);
|
|
1411
|
+
* });
|
|
1412
|
+
* });
|
|
1413
|
+
* ```
|
|
1414
|
+
*
|
|
1415
|
+
* Any additional parameters passed to the enqueued function will
|
|
1115
1416
|
* be remembered and passed through when the function is executed.
|
|
1116
1417
|
*
|
|
1117
|
-
* The `queue` array is cleared at the very end of
|
|
1118
|
-
* the environment's tick cycle.
|
|
1119
|
-
* @param {Function} enqueuedRule
|
|
1120
1418
|
* @deprecated since version 0.5.14
|
|
1121
1419
|
* @since 0.0.5
|
|
1122
1420
|
*/
|
|
@@ -1133,7 +1431,7 @@
|
|
|
1133
1431
|
};
|
|
1134
1432
|
/**
|
|
1135
1433
|
* From a RuleObj, execute a single rule (function or structured Rule).
|
|
1136
|
-
* @
|
|
1434
|
+
* @hidden
|
|
1137
1435
|
*/
|
|
1138
1436
|
Agent.prototype.executeRule = function (ruleObj) {
|
|
1139
1437
|
var rule = ruleObj.rule, args = ruleObj.args;
|
|
@@ -1148,6 +1446,7 @@
|
|
|
1148
1446
|
};
|
|
1149
1447
|
/**
|
|
1150
1448
|
* Execute all rules.
|
|
1449
|
+
* @hidden
|
|
1151
1450
|
*/
|
|
1152
1451
|
Agent.prototype.executeRules = function () {
|
|
1153
1452
|
var _this = this;
|
|
@@ -1164,6 +1463,7 @@
|
|
|
1164
1463
|
};
|
|
1165
1464
|
/**
|
|
1166
1465
|
* Execute all enqueued rules.
|
|
1466
|
+
* @hidden
|
|
1167
1467
|
*/
|
|
1168
1468
|
Agent.prototype.executeEnqueuedRules = function () {
|
|
1169
1469
|
// if new data from the rules
|
|
@@ -1193,9 +1493,15 @@
|
|
|
1193
1493
|
}());
|
|
1194
1494
|
|
|
1195
1495
|
/**
|
|
1196
|
-
*
|
|
1197
|
-
*
|
|
1198
|
-
*
|
|
1496
|
+
* Return the mean value from an array of numbers.
|
|
1497
|
+
*
|
|
1498
|
+
* ```js
|
|
1499
|
+
* mean([1, 2, 3]); // returns 2
|
|
1500
|
+
* mean([10]); // returns 10
|
|
1501
|
+
*
|
|
1502
|
+
* mean([]); // returns null for empty arrays
|
|
1503
|
+
* ```
|
|
1504
|
+
*
|
|
1199
1505
|
* @since 0.0.16
|
|
1200
1506
|
*/
|
|
1201
1507
|
function mean(arr) {
|
|
@@ -1230,6 +1536,7 @@
|
|
|
1230
1536
|
}
|
|
1231
1537
|
|
|
1232
1538
|
/**
|
|
1539
|
+
* @hidden
|
|
1233
1540
|
* @since 0.3.11
|
|
1234
1541
|
*/
|
|
1235
1542
|
var NumArray = /** @class */ (function () {
|
|
@@ -1241,7 +1548,7 @@
|
|
|
1241
1548
|
get: function () {
|
|
1242
1549
|
return this._index;
|
|
1243
1550
|
},
|
|
1244
|
-
enumerable:
|
|
1551
|
+
enumerable: false,
|
|
1245
1552
|
configurable: true
|
|
1246
1553
|
});
|
|
1247
1554
|
NumArray.prototype.set = function (i, n) {
|
|
@@ -1292,24 +1599,33 @@
|
|
|
1292
1599
|
}());
|
|
1293
1600
|
|
|
1294
1601
|
/**
|
|
1602
|
+
* A `Network` allows {@linkcode Agent}s to be connected to each other.
|
|
1295
1603
|
* @since 0.1.3
|
|
1296
1604
|
*/
|
|
1297
1605
|
var Network = /** @class */ (function () {
|
|
1298
1606
|
function Network() {
|
|
1607
|
+
/** @hidden */
|
|
1299
1608
|
this.adjacencyList = new Map();
|
|
1300
|
-
|
|
1609
|
+
/**
|
|
1610
|
+
* instantiated and updated in _resetAdjacencyMatrix
|
|
1611
|
+
* @hidden
|
|
1612
|
+
*/
|
|
1301
1613
|
this.adjacencyMatrix = null;
|
|
1302
1614
|
/**
|
|
1303
|
-
*
|
|
1304
|
-
* in the order they were added
|
|
1615
|
+
* An array of the {@linkcode Agent}s in this `Network`
|
|
1616
|
+
* (in the order they were added).
|
|
1305
1617
|
*/
|
|
1306
1618
|
this.agents = [];
|
|
1307
1619
|
}
|
|
1308
1620
|
/**
|
|
1309
1621
|
* Add an agent to the network.
|
|
1310
|
-
* Returns `true` if the
|
|
1311
|
-
*
|
|
1312
|
-
*
|
|
1622
|
+
* @returns Returns `true` if the `Agent` was successfully added, `false` otherwise.
|
|
1623
|
+
*
|
|
1624
|
+
* ```js
|
|
1625
|
+
* const a = new Agent();
|
|
1626
|
+
* network.addAgent(a); // returns true
|
|
1627
|
+
* network.addAgent(a); // returns false since `a` was already in the `Network`
|
|
1628
|
+
* ```
|
|
1313
1629
|
* @since 0.1.3
|
|
1314
1630
|
*/
|
|
1315
1631
|
Network.prototype.addAgent = function (agent) {
|
|
@@ -1322,8 +1638,8 @@
|
|
|
1322
1638
|
return false;
|
|
1323
1639
|
};
|
|
1324
1640
|
/**
|
|
1325
|
-
*
|
|
1326
|
-
*
|
|
1641
|
+
* Given an {@linkcode Environment}, add all the {@linkcode Agent}s in that `Environment`
|
|
1642
|
+
* to this `Network`. (This is a shortcut for calling `environment.getAgents().forEach(a => network.addAgent(a)));`)
|
|
1327
1643
|
* @since 0.2.1
|
|
1328
1644
|
*/
|
|
1329
1645
|
Network.prototype.addFromEnvironment = function (environment) {
|
|
@@ -1331,10 +1647,20 @@
|
|
|
1331
1647
|
environment.getAgents().forEach(function (agent) { return _this.addAgent(agent); });
|
|
1332
1648
|
};
|
|
1333
1649
|
/**
|
|
1334
|
-
*
|
|
1335
|
-
*
|
|
1650
|
+
* Removes an {@linkcode Agent} from the `Network`.
|
|
1651
|
+
*
|
|
1652
|
+
* ```js
|
|
1653
|
+
* const a = new Agent();
|
|
1654
|
+
* network.addAgent(a);
|
|
1655
|
+
*
|
|
1656
|
+
* network.removeAgent(a); // returns true
|
|
1657
|
+
*
|
|
1658
|
+
* network.removeAgent(a); // returns false since `a` was no longer in the `Network`
|
|
1659
|
+
* ```
|
|
1660
|
+
*
|
|
1661
|
+
* @returns Returns `true` if the agent was successfully removed.
|
|
1662
|
+
*
|
|
1336
1663
|
* Returns `false` if the agent was not in the network to begin with.
|
|
1337
|
-
* @param {Agent} agent
|
|
1338
1664
|
* @since 0.1.3
|
|
1339
1665
|
*/
|
|
1340
1666
|
Network.prototype.removeAgent = function (agent) {
|
|
@@ -1354,7 +1680,17 @@
|
|
|
1354
1680
|
return true;
|
|
1355
1681
|
};
|
|
1356
1682
|
/**
|
|
1357
|
-
* Removes all
|
|
1683
|
+
* Removes all {@linkcode Agent}s from the `Network`.
|
|
1684
|
+
*
|
|
1685
|
+
* ```js
|
|
1686
|
+
* const network = new Network();
|
|
1687
|
+
* network.addAgent(new Agent());
|
|
1688
|
+
* network.size(); // returns 1
|
|
1689
|
+
*
|
|
1690
|
+
* network.clear();
|
|
1691
|
+
* network.size(); // returns 0
|
|
1692
|
+
* ```
|
|
1693
|
+
*
|
|
1358
1694
|
* @since 0.2.1
|
|
1359
1695
|
*/
|
|
1360
1696
|
Network.prototype.clear = function () {
|
|
@@ -1364,23 +1700,36 @@
|
|
|
1364
1700
|
}
|
|
1365
1701
|
};
|
|
1366
1702
|
/**
|
|
1367
|
-
*
|
|
1368
|
-
*
|
|
1369
|
-
*
|
|
1370
|
-
*
|
|
1371
|
-
*
|
|
1703
|
+
* Attempts to create a connection between {@linkcode Agent}s `a` and `b`.
|
|
1704
|
+
* @returns Returns `true` if the connection was successfully created (i.e. if `a` and `b` were previously not connected and now are).
|
|
1705
|
+
*
|
|
1706
|
+
* ```js
|
|
1707
|
+
* const a = new Agent();
|
|
1708
|
+
* const b = new Agent();
|
|
1709
|
+
* network.addAgent(a);
|
|
1710
|
+
* network.addAgent(b);
|
|
1711
|
+
*
|
|
1712
|
+
* network.connect(a, b); // returns true
|
|
1713
|
+
*
|
|
1714
|
+
* network.connect(a, b); // returns false since they are now already connected
|
|
1715
|
+
*
|
|
1716
|
+
* const c = new Agent();
|
|
1717
|
+
* network.connect(a, c); // returns false since `c` is not in the `Network`
|
|
1718
|
+
* ```
|
|
1719
|
+
*
|
|
1720
|
+
* Returns `false` otherwise, for example if `a` and `b` are the same `Agent`, or if either is not in the `Network`.
|
|
1372
1721
|
* @since 0.1.3
|
|
1373
1722
|
*/
|
|
1374
|
-
Network.prototype.connect = function (
|
|
1375
|
-
if (
|
|
1723
|
+
Network.prototype.connect = function (a, b) {
|
|
1724
|
+
if (a === b)
|
|
1376
1725
|
return false;
|
|
1377
|
-
if (!this.isInNetwork(
|
|
1726
|
+
if (!this.isInNetwork(a) || !this.isInNetwork(b))
|
|
1378
1727
|
return false;
|
|
1379
|
-
if (!this.areConnected(
|
|
1380
|
-
this.adjacencyList.get(
|
|
1381
|
-
this.adjacencyList.get(
|
|
1382
|
-
var i1 = this.indexOf(
|
|
1383
|
-
var i2 = this.indexOf(
|
|
1728
|
+
if (!this.areConnected(a, b)) {
|
|
1729
|
+
this.adjacencyList.get(a).push(b);
|
|
1730
|
+
this.adjacencyList.get(b).push(a);
|
|
1731
|
+
var i1 = this.indexOf(a);
|
|
1732
|
+
var i2 = this.indexOf(b);
|
|
1384
1733
|
this.adjacencyMatrix.set(i1, i2, 1);
|
|
1385
1734
|
this.adjacencyMatrix.set(i2, i1, 1);
|
|
1386
1735
|
return true;
|
|
@@ -1388,24 +1737,42 @@
|
|
|
1388
1737
|
return false;
|
|
1389
1738
|
};
|
|
1390
1739
|
/**
|
|
1391
|
-
* Returns `true` if
|
|
1392
|
-
*
|
|
1393
|
-
*
|
|
1740
|
+
* @returns Returns `true` if {@linkcode Agent}s `a` and `b` are connected, `false` if they are not.
|
|
1741
|
+
*
|
|
1742
|
+
* ```js
|
|
1743
|
+
* network.connect(a, b);
|
|
1744
|
+
* network.areConnected(a, b); // returns true since they have been connected
|
|
1745
|
+
*
|
|
1746
|
+
* network.disconnect(a, b);
|
|
1747
|
+
* network.areConnected(a, b); // returns false since they have been disconnected
|
|
1748
|
+
* ```
|
|
1749
|
+
*
|
|
1394
1750
|
* @since 0.1.3
|
|
1395
1751
|
*/
|
|
1396
|
-
Network.prototype.areConnected = function (
|
|
1397
|
-
if (!this.isInNetwork(
|
|
1752
|
+
Network.prototype.areConnected = function (a, b) {
|
|
1753
|
+
if (!this.isInNetwork(a) || !this.isInNetwork(b))
|
|
1398
1754
|
return false;
|
|
1399
|
-
var i1 = this.indexOf(
|
|
1400
|
-
var i2 = this.indexOf(
|
|
1755
|
+
var i1 = this.indexOf(a);
|
|
1756
|
+
var i2 = this.indexOf(b);
|
|
1401
1757
|
return (this.adjacencyMatrix.get(i1, i2) === 1 &&
|
|
1402
1758
|
this.adjacencyMatrix.get(i2, i1) === 1);
|
|
1403
1759
|
};
|
|
1404
1760
|
/**
|
|
1405
|
-
*
|
|
1406
|
-
*
|
|
1407
|
-
*
|
|
1408
|
-
*
|
|
1761
|
+
* Attempts to sever the connection between {@linkcode Agent}s `a` and `b`.
|
|
1762
|
+
* @returns Returns `true` if the `Agent`s were successfully disconnected, `false` otherwise.
|
|
1763
|
+
*
|
|
1764
|
+
* ```js
|
|
1765
|
+
* const a = new Agent();
|
|
1766
|
+
* const b = new Agent();
|
|
1767
|
+
* network.addAgent(a);
|
|
1768
|
+
* network.addAgent(b);
|
|
1769
|
+
*
|
|
1770
|
+
* network.connect(a, b);
|
|
1771
|
+
* network.disconnect(a, b); // returns true since they were connected and are no longer
|
|
1772
|
+
*
|
|
1773
|
+
* network.disconnect(a, b); // returns false since they were already not connected
|
|
1774
|
+
* ```
|
|
1775
|
+
*
|
|
1409
1776
|
* @since 0.1.3
|
|
1410
1777
|
*/
|
|
1411
1778
|
Network.prototype.disconnect = function (a1, a2) {
|
|
@@ -1425,40 +1792,50 @@
|
|
|
1425
1792
|
return false;
|
|
1426
1793
|
};
|
|
1427
1794
|
/**
|
|
1428
|
-
*
|
|
1795
|
+
* @returns Returns the number of {@linkcode Agent}s in the `Network`.
|
|
1796
|
+
*
|
|
1797
|
+
* ```js
|
|
1798
|
+
* const a = new Agent();
|
|
1799
|
+
* const b = new Agent();
|
|
1800
|
+
* const c = new Agent();
|
|
1801
|
+
* [a, b, c].forEach(agt => network.addAgent(agt));
|
|
1802
|
+
*
|
|
1803
|
+
* network.size(); // returns 3
|
|
1804
|
+
* ```
|
|
1805
|
+
*
|
|
1429
1806
|
* @since 0.1.3
|
|
1430
1807
|
*/
|
|
1431
1808
|
Network.prototype.size = function () {
|
|
1432
1809
|
return this.agents.length;
|
|
1433
1810
|
};
|
|
1434
1811
|
/**
|
|
1435
|
-
*
|
|
1436
|
-
* and invoke the callback
|
|
1437
|
-
* @param {Function} cb
|
|
1812
|
+
* Loop over all the {@linkcode Agent}s in the `Network` (in the order they were added),
|
|
1813
|
+
* and invoke the `callback` function with the `Agent` and an index passed as parameters.
|
|
1438
1814
|
* @since 0.1.3
|
|
1439
1815
|
*/
|
|
1440
|
-
Network.prototype.forEach = function (
|
|
1441
|
-
this.agents.forEach(
|
|
1816
|
+
Network.prototype.forEach = function (callback) {
|
|
1817
|
+
this.agents.forEach(callback);
|
|
1442
1818
|
};
|
|
1443
1819
|
/**
|
|
1444
|
-
*
|
|
1445
|
-
* @param {Function} cb
|
|
1820
|
+
* The same method as {@linkcode forEach}, but executes in random order.
|
|
1446
1821
|
* @since 0.1.3
|
|
1447
1822
|
*/
|
|
1448
|
-
Network.prototype.forEachRand = function (
|
|
1449
|
-
shuffle(this.agents).forEach(
|
|
1823
|
+
Network.prototype.forEachRand = function (callback) {
|
|
1824
|
+
shuffle(this.agents).forEach(callback);
|
|
1450
1825
|
};
|
|
1451
1826
|
/**
|
|
1452
|
-
* Returns true if the
|
|
1453
|
-
* @param {Agent} agent
|
|
1827
|
+
* Returns `true` if the given {@linkcode Agent} is in the `Network`, `false` if it is not.
|
|
1454
1828
|
* @since 0.1.3
|
|
1455
1829
|
*/
|
|
1456
1830
|
Network.prototype.isInNetwork = function (agent) {
|
|
1457
1831
|
return this.adjacencyList.has(agent);
|
|
1458
1832
|
};
|
|
1459
1833
|
/**
|
|
1460
|
-
*
|
|
1461
|
-
*
|
|
1834
|
+
* Returns the {@linkcode Agent} at index `i`, where `i = 0` is the first `Agent`
|
|
1835
|
+
* added to the `Network`, `i = 1` the second, etc.
|
|
1836
|
+
*
|
|
1837
|
+
* Negative indices are allowed, so `network.get(-1)` returns the `Agent` that was most recently
|
|
1838
|
+
* added to the `Network`, `-2` the second-most recent, etc.
|
|
1462
1839
|
* @since 0.1.3
|
|
1463
1840
|
*/
|
|
1464
1841
|
Network.prototype.get = function (i) {
|
|
@@ -1469,17 +1846,26 @@
|
|
|
1469
1846
|
return this.agents[i];
|
|
1470
1847
|
};
|
|
1471
1848
|
/**
|
|
1472
|
-
*
|
|
1473
|
-
* @param {Agent} agent
|
|
1849
|
+
* Returns the index of the given {@linkcode Agent} in the {@linkcode agents} array.
|
|
1474
1850
|
* @since 0.1.3
|
|
1475
1851
|
*/
|
|
1476
1852
|
Network.prototype.indexOf = function (agent) {
|
|
1477
1853
|
return this.agents.indexOf(agent);
|
|
1478
1854
|
};
|
|
1479
1855
|
/**
|
|
1480
|
-
*
|
|
1481
|
-
*
|
|
1482
|
-
*
|
|
1856
|
+
* Returns an array of {@linkcode Agent}s that are connected to the given `Agent` (in no guaranteed order).
|
|
1857
|
+
*
|
|
1858
|
+
* Returns `null` if the given `Agent` is not in the `Network`.
|
|
1859
|
+
*
|
|
1860
|
+
* ```js
|
|
1861
|
+
* // suppose a, b, and c are connected
|
|
1862
|
+
* network.neighbors(a); // returns [b, c] (or [c, b])
|
|
1863
|
+
*
|
|
1864
|
+
* network.disconnect(a, c);
|
|
1865
|
+
* network.neighbors(a); // returns [b]
|
|
1866
|
+
* network.neighbors(c); // returns [b]
|
|
1867
|
+
* ```
|
|
1868
|
+
*
|
|
1483
1869
|
* @since 0.1.3
|
|
1484
1870
|
*/
|
|
1485
1871
|
Network.prototype.neighbors = function (agent) {
|
|
@@ -1488,7 +1874,7 @@
|
|
|
1488
1874
|
return this.adjacencyList.get(agent);
|
|
1489
1875
|
};
|
|
1490
1876
|
/**
|
|
1491
|
-
*
|
|
1877
|
+
* Draw a connection between every pair of {@linkcode Agent}s in the `Network`.
|
|
1492
1878
|
* @since 0.1.3
|
|
1493
1879
|
*/
|
|
1494
1880
|
Network.prototype.complete = function () {
|
|
@@ -1501,6 +1887,7 @@
|
|
|
1501
1887
|
/**
|
|
1502
1888
|
* Internal helper function to reset the adjacencyMatrix.
|
|
1503
1889
|
* This gets called when agents are added to or removed from the network.
|
|
1890
|
+
* @hidden
|
|
1504
1891
|
*/
|
|
1505
1892
|
Network.prototype._resetAdjacencyMatrix = function () {
|
|
1506
1893
|
var size = this.size();
|
|
@@ -1517,11 +1904,7 @@
|
|
|
1517
1904
|
this.adjacencyMatrix = newMatrix;
|
|
1518
1905
|
};
|
|
1519
1906
|
/**
|
|
1520
|
-
* Returns `true` if a, b, and c
|
|
1521
|
-
* if (at least) one of the three is connected to the other two.
|
|
1522
|
-
* @param {Agent} a
|
|
1523
|
-
* @param {Agent} b
|
|
1524
|
-
* @param {Agent} c
|
|
1907
|
+
* Returns `true` if `Agent`s a, b, and c form a 'triplet' — if (at least) one of the three is connected to the other two. Returns `false` otherwise.
|
|
1525
1908
|
* @since 0.5.17
|
|
1526
1909
|
*/
|
|
1527
1910
|
Network.prototype.isTriplet = function (a, b, c) {
|
|
@@ -1535,11 +1918,7 @@
|
|
|
1535
1918
|
return connections >= 2;
|
|
1536
1919
|
};
|
|
1537
1920
|
/**
|
|
1538
|
-
* Returns `true` if a, b, and c
|
|
1539
|
-
* each connected to the other two.
|
|
1540
|
-
* @param {Agent} a
|
|
1541
|
-
* @param {Agent} b
|
|
1542
|
-
* @param {Agent} c
|
|
1921
|
+
* Returns `true` if `Agent`s a, b, and c form a 'closed triplet' — if each of the three are connected to the other two. Returns `false` otherwise.
|
|
1543
1922
|
* @since 0.5.17
|
|
1544
1923
|
*/
|
|
1545
1924
|
Network.prototype.isClosedTriplet = function (a, b, c) {
|
|
@@ -1552,6 +1931,7 @@
|
|
|
1552
1931
|
].filter(function (v) { return v; }).length;
|
|
1553
1932
|
return connections === 3;
|
|
1554
1933
|
};
|
|
1934
|
+
/** @hidden */
|
|
1555
1935
|
Network.prototype._globalClusteringCoefficient = function () {
|
|
1556
1936
|
var _this = this;
|
|
1557
1937
|
var triplets = 0;
|
|
@@ -1573,12 +1953,15 @@
|
|
|
1573
1953
|
return closedTriplets / triplets;
|
|
1574
1954
|
};
|
|
1575
1955
|
/**
|
|
1576
|
-
*
|
|
1577
|
-
*
|
|
1578
|
-
*
|
|
1579
|
-
* If
|
|
1580
|
-
*
|
|
1581
|
-
*
|
|
1956
|
+
* The {@link https://en.wikipedia.org/wiki/Clustering_coefficient | clustering coefficient} is a measure of how
|
|
1957
|
+
* closely connected either an individual {@linkcode Agent}'s connections are or the `Network` as a whole is.
|
|
1958
|
+
*
|
|
1959
|
+
* If an `Agent` is passed as the single parameter, returns the {@link https://en.wikipedia.org/wiki/Clustering_coefficient#Local_clustering_coefficient | local
|
|
1960
|
+
* clustering coefficient} for that `Agent`.
|
|
1961
|
+
*
|
|
1962
|
+
* If no parameter is passed, returns the {@link https://en.wikipedia.org/wiki/Clustering_coefficient#Global_clustering_coefficient | global clustering coefficient}
|
|
1963
|
+
* of the `Network` (an aggregate measure of how connected the `Agent`s are).
|
|
1964
|
+
*
|
|
1582
1965
|
* @since 0.5.17
|
|
1583
1966
|
*/
|
|
1584
1967
|
Network.prototype.clusteringCoefficient = function (agent) {
|
|
@@ -1602,9 +1985,11 @@
|
|
|
1602
1985
|
return (2 * clusterConnections) / (k * (k - 1));
|
|
1603
1986
|
};
|
|
1604
1987
|
/**
|
|
1605
|
-
* Returns the average clustering coefficient for the
|
|
1606
|
-
* of the local clustering coefficient across all
|
|
1607
|
-
*
|
|
1988
|
+
* Returns the {@link https://en.wikipedia.org/wiki/Clustering_coefficient#Network_average_clustering_coefficient | average clustering coefficient} for the `Network` (the average
|
|
1989
|
+
* of the {@link Network.clusteringCoefficient | local clustering coefficient} across all `Agent`s).
|
|
1990
|
+
*
|
|
1991
|
+
* Note that this is a different measurement from the _global_ clustering coefficient
|
|
1992
|
+
* (i.e. calling {@linkcode clusteringCoefficient} without any parameters).
|
|
1608
1993
|
* @since 0.5.17
|
|
1609
1994
|
*/
|
|
1610
1995
|
Network.prototype.averageClusteringCoefficient = function () {
|
|
@@ -1660,6 +2045,14 @@
|
|
|
1660
2045
|
}());
|
|
1661
2046
|
|
|
1662
2047
|
/**
|
|
2048
|
+
* Return the minimum value from an array of numbers.
|
|
2049
|
+
*
|
|
2050
|
+
* ```js
|
|
2051
|
+
* min([1, 2, 3]); // returns 1
|
|
2052
|
+
* min([10]); // returns 10
|
|
2053
|
+
*
|
|
2054
|
+
* min([]); // returns null for empty arrays
|
|
2055
|
+
* ```
|
|
1663
2056
|
* @since 0.2.0
|
|
1664
2057
|
*/
|
|
1665
2058
|
function min(arr) {
|
|
@@ -1669,6 +2062,15 @@
|
|
|
1669
2062
|
}
|
|
1670
2063
|
|
|
1671
2064
|
/**
|
|
2065
|
+
* Return the maximum value from an array of numbers.
|
|
2066
|
+
*
|
|
2067
|
+
* ```js
|
|
2068
|
+
* max([1, 2, 3]); // returns 3
|
|
2069
|
+
* max([10]); // returns 10
|
|
2070
|
+
*
|
|
2071
|
+
* max([]); // returns null for empty arrays
|
|
2072
|
+
* ```
|
|
2073
|
+
*
|
|
1672
2074
|
* @since 0.2.0
|
|
1673
2075
|
*/
|
|
1674
2076
|
function max(arr) {
|
|
@@ -1710,8 +2112,16 @@
|
|
|
1710
2112
|
}
|
|
1711
2113
|
|
|
1712
2114
|
/**
|
|
1713
|
-
*
|
|
1714
|
-
*
|
|
2115
|
+
* Return the mean value from an array of numbers.
|
|
2116
|
+
*
|
|
2117
|
+
* ```js
|
|
2118
|
+
* median([1, 2, 3]); // returns 2
|
|
2119
|
+
* median([10]); // returns 10
|
|
2120
|
+
* median([1, 2, 3, 4]); // returns 2.5 (the mean of the two median values)
|
|
2121
|
+
*
|
|
2122
|
+
* median([]); // returns null for empty arrays
|
|
2123
|
+
* ```
|
|
2124
|
+
*
|
|
1715
2125
|
* @param {number[]} arr
|
|
1716
2126
|
* @since 0.2.0
|
|
1717
2127
|
*/
|
|
@@ -1719,6 +2129,9 @@
|
|
|
1719
2129
|
return percentile(arr, 0.5);
|
|
1720
2130
|
}
|
|
1721
2131
|
|
|
2132
|
+
function isMultipleSampleFunc(f) {
|
|
2133
|
+
return f([1]).length > 0;
|
|
2134
|
+
}
|
|
1722
2135
|
var sample;
|
|
1723
2136
|
/**
|
|
1724
2137
|
* Gets a random element from `array`.
|
|
@@ -1808,12 +2221,21 @@
|
|
|
1808
2221
|
|
|
1809
2222
|
/// <reference path="../types/Point.d.ts" />
|
|
1810
2223
|
/**
|
|
1811
|
-
* Finds the distance between `p1` and `p2`.
|
|
1812
|
-
*
|
|
1813
|
-
* `x`, `y`, and/or `z`
|
|
1814
|
-
* @
|
|
1815
|
-
*
|
|
1816
|
-
*
|
|
2224
|
+
* Finds the distance between `p1` and `p2`.
|
|
2225
|
+
*
|
|
2226
|
+
* The inputs may be plain objects with `x`, `y`, and/or `z` keys, {@linkcode Vector}s,
|
|
2227
|
+
* or {@linkcode Agent}s with `x`, `y`, and/or `z` data.
|
|
2228
|
+
*
|
|
2229
|
+
* ```js
|
|
2230
|
+
* const a1 = new Agent();
|
|
2231
|
+
* const a2 = new Agent({ x: 3, y: 4 });
|
|
2232
|
+
* distance(a1, a2); // returns 5 (defaults to x = 0 and y = 0 for a1)
|
|
2233
|
+
*
|
|
2234
|
+
* const p1 = { x: 0, y: 2 };
|
|
2235
|
+
* const p2 = { x: 0, y: 4 };
|
|
2236
|
+
* distance(p1, p2); // returns 2
|
|
2237
|
+
* ```
|
|
2238
|
+
*
|
|
1817
2239
|
* @since 0.0.10
|
|
1818
2240
|
*/
|
|
1819
2241
|
function distance(p1, p2) {
|
|
@@ -2111,6 +2533,14 @@
|
|
|
2111
2533
|
}());
|
|
2112
2534
|
|
|
2113
2535
|
/**
|
|
2536
|
+
* Finds the {@link https://en.wikipedia.org/wiki/Greatest_common_divisor | greatest common divisor} of `a` and `b`.
|
|
2537
|
+
*
|
|
2538
|
+
* ```js
|
|
2539
|
+
* gcd(7, 13); // returns 1
|
|
2540
|
+
* gcd(9, 15); // returns 3
|
|
2541
|
+
* gcd(12, 24); // returns 12
|
|
2542
|
+
* ```
|
|
2543
|
+
*
|
|
2114
2544
|
* @since 0.4.5
|
|
2115
2545
|
*/
|
|
2116
2546
|
function gcd(a, b) {
|
|
@@ -2179,32 +2609,80 @@
|
|
|
2179
2609
|
grayscale: false,
|
|
2180
2610
|
scale: 1
|
|
2181
2611
|
};
|
|
2612
|
+
var BLACK = { r: 0, g: 0, b: 0, a: 255 };
|
|
2613
|
+
var WHITE = { r: 255, g: 255, b: 255, a: 255 };
|
|
2614
|
+
var RED = { r: 255, g: 0, b: 0, a: 255 };
|
|
2615
|
+
var MAROON = { r: 127, g: 0, b: 0, a: 255 };
|
|
2616
|
+
var YELLOW = { r: 255, g: 255, b: 0, a: 255 };
|
|
2617
|
+
var BLUE = { r: 0, g: 0, b: 255, a: 255 };
|
|
2618
|
+
var GREEN = { r: 0, g: 127, b: 0, a: 255 };
|
|
2619
|
+
var LIME = { r: 0, g: 255, b: 0, a: 255 };
|
|
2620
|
+
var AQUA = { r: 0, g: 255, b: 255, a: 255 };
|
|
2621
|
+
var ORANGE = { r: 255, g: 165, b: 0, a: 255 };
|
|
2622
|
+
var FUCHSIA = { r: 255, g: 0, b: 255, a: 255 };
|
|
2623
|
+
var PURPLE = { r: 127, g: 0, b: 127, a: 255 };
|
|
2182
2624
|
/**
|
|
2625
|
+
* Each static member of the `Colors` class (e.g. `Colors.GREEN`, `Colors.RED`) is a pixel-like object with `r`, `g`, `b`, and `a` values that range from `0` to `255`.
|
|
2183
2626
|
* @since 0.4.0
|
|
2184
2627
|
*/
|
|
2185
|
-
var Colors = {
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2628
|
+
var Colors = /** @class */ (function () {
|
|
2629
|
+
function Colors() {
|
|
2630
|
+
}
|
|
2631
|
+
/** <div style="width: 100%; height: 20px; background-color: rgb(0, 0, 0);"></div> */
|
|
2632
|
+
Colors.BLACK = BLACK;
|
|
2633
|
+
/** <div style="width: 100%; height: 20px; background-color: rgb(255, 255, 255); border: 1px solid #eee;"></div> */
|
|
2634
|
+
Colors.WHITE = WHITE;
|
|
2635
|
+
/** <div style="width: 100%; height: 20px; background-color: rgb(255, 0, 0);"></div> */
|
|
2636
|
+
Colors.RED = RED;
|
|
2637
|
+
/** <div style="width: 100%; height: 20px; background-color: rgb(127, 0, 0);"></div> */
|
|
2638
|
+
Colors.MAROON = MAROON;
|
|
2639
|
+
/** <div style="width: 100%; height: 20px; background-color: rgb(255,255, 0);"></div> */
|
|
2640
|
+
Colors.YELLOW = YELLOW;
|
|
2641
|
+
/** <div style="width: 100%; height: 20px; background-color: rgb(0, 0, 255);"></div> */
|
|
2642
|
+
Colors.BLUE = BLUE;
|
|
2643
|
+
/** <div style="width: 100%; height: 20px; background-color: rgb(0, 127, 0);"></div> */
|
|
2644
|
+
Colors.GREEN = GREEN;
|
|
2645
|
+
/** <div style="width: 100%; height: 20px; background-color: rgb(0, 255, 0);"></div> */
|
|
2646
|
+
Colors.LIME = LIME;
|
|
2647
|
+
/** <div style="width: 100%; height: 20px; background-color: rgb(0, 255, 255);"></div> */
|
|
2648
|
+
Colors.AQUA = AQUA;
|
|
2649
|
+
/** <div style="width: 100%; height: 20px; background-color: rgb(255, 165, 0);"></div> */
|
|
2650
|
+
Colors.ORANGE = ORANGE;
|
|
2651
|
+
/** <div style="width: 100%; height: 20px; background-color: rgb(255, 0, 255);"></div> */
|
|
2652
|
+
Colors.FUCHSIA = FUCHSIA;
|
|
2653
|
+
/** <div style="width: 100%; height: 20px; background-color: rgb(127, 0, 127);"></div> */
|
|
2654
|
+
Colors.PURPLE = PURPLE;
|
|
2655
|
+
return Colors;
|
|
2656
|
+
}());
|
|
2199
2657
|
/**
|
|
2658
|
+
* The `Terrain` class lets {@linkcode Environment}s function as lattices upon which {@link https://en.wikipedia.org/wiki/Cellular_automaton | cellular automata} can grow. With a `Terrain`, {@linkcode Agent}s may not be necessary, since all the cells of a `Terrain` can follow update rules (similar to but simplified from `Agent`s).
|
|
2659
|
+
*
|
|
2660
|
+
* ### Usage
|
|
2661
|
+
*
|
|
2662
|
+
* ```js
|
|
2663
|
+
* const environment = new Environment();
|
|
2664
|
+
* const terrain = new Terrain(30, 30); // create a 30 x 30 Terrain
|
|
2665
|
+
* environment.use(terrain); // tell the Environment to 'use' this Terrain as a helper
|
|
2666
|
+
* ```
|
|
2667
|
+
*
|
|
2200
2668
|
* @since 0.4.0
|
|
2201
2669
|
*/
|
|
2202
2670
|
var Terrain = /** @class */ (function () {
|
|
2203
2671
|
/**
|
|
2672
|
+
* Instantiate a new `Terrain` by passing its `width` and `height` as the first two parameters, and an optional configuration object as the third.
|
|
2673
|
+
*
|
|
2674
|
+
* ### Options
|
|
2675
|
+
*
|
|
2676
|
+
* - `async` (*boolean* = `false`) — Whether to run the `Terrain` in synchronous (`true`) or asynchronous (`mode`). Defaults to synchronous. Depending on the timing mode, {@link addRule | Terrain update rules} should be written differently.
|
|
2677
|
+
* - `grayscale` (*boolean* = `false`)
|
|
2678
|
+
* - In **color mode** (the default), each cell of a `Terrain` is represented by a {@link Colors | pixel-like object} (an object with numeric keys `r`, `g`, `b`, and `a` ranging from 0-255).
|
|
2679
|
+
* - In **grayscale mode**, each cell of a `Terrain` is represented by a single number ranging from 0 (black) to 255 (white).
|
|
2680
|
+
* - `scale` (*number* = `1`) — The size, in pixels, of each cell's width and height when the `Terrain` is rendered using a {@linkcode CanvasRenderer}. In the below screenshots, the `Terrain` on the left uses a scale of `1` while the one on the right uses a scale of `5`:
|
|
2204
2681
|
*
|
|
2205
|
-
*
|
|
2206
|
-
*
|
|
2207
|
-
*
|
|
2682
|
+
* <img alt="Terrain with scale = 1" style="width: 49%;" src="https://cms.flocc.network/wp-content/uploads/2020/04/terrain-1.png">
|
|
2683
|
+
* <img alt="Terrain with scale = 5" style="width: 49%;" src="https://cms.flocc.network/wp-content/uploads/2020/04/terrain-5.png">
|
|
2684
|
+
*
|
|
2685
|
+
* In addition to the above setup, you will need to {@link init | initialize} the `Terrain` and {@link addRule | add an update rule}.
|
|
2208
2686
|
*/
|
|
2209
2687
|
function Terrain(width, height, options) {
|
|
2210
2688
|
if (options === void 0) { options = defaultTerrainOptions; }
|
|
@@ -2224,9 +2702,16 @@
|
|
|
2224
2702
|
this.nextData = new Uint8ClampedArray(this.data);
|
|
2225
2703
|
}
|
|
2226
2704
|
/**
|
|
2227
|
-
* Initialize (or overwrite)
|
|
2228
|
-
*
|
|
2229
|
-
*
|
|
2705
|
+
* Initialize (or overwrite) all cell values. The rule you pass has the same signature
|
|
2706
|
+
* as {@linkcode addRule}, but should always return a value (either a number or {@linkcode Colors | pixel-like object}).
|
|
2707
|
+
*
|
|
2708
|
+
* ```js
|
|
2709
|
+
* // initializes cells randomly to either blue or red
|
|
2710
|
+
* terrain.init((x, y) => {
|
|
2711
|
+
* return utils.uniform() > 0.5 ? Colors.BLUE : Colors.RED;
|
|
2712
|
+
* });
|
|
2713
|
+
* ```
|
|
2714
|
+
*
|
|
2230
2715
|
* @since 0.4.0
|
|
2231
2716
|
*/
|
|
2232
2717
|
Terrain.prototype.init = function (rule) {
|
|
@@ -2249,12 +2734,41 @@
|
|
|
2249
2734
|
}
|
|
2250
2735
|
}
|
|
2251
2736
|
}
|
|
2737
|
+
this.nextData = new Uint8ClampedArray(this.data);
|
|
2252
2738
|
};
|
|
2253
2739
|
/**
|
|
2254
|
-
*
|
|
2255
|
-
* passed as the rule should be called with the parameters (x
|
|
2256
|
-
* a
|
|
2257
|
-
*
|
|
2740
|
+
* Similar to adding behavior to {@linkcode Agent}s, this adds an update rule for the `Terrain`.
|
|
2741
|
+
* The function passed as the rule should be called with the parameters (`x`, `y`). In synchronous mode,
|
|
2742
|
+
* it should return a value that is the color of that cell on the next time step.
|
|
2743
|
+
*
|
|
2744
|
+
* ```js
|
|
2745
|
+
* // turns a cell red if the x-value is greater than 200,
|
|
2746
|
+
* // blue if the x-value is less than 100,
|
|
2747
|
+
* // and leaves it unchanged in between
|
|
2748
|
+
* terrain.addRule((x, y) => {
|
|
2749
|
+
* if (x > 200) {
|
|
2750
|
+
* return Colors.RED;
|
|
2751
|
+
* } else if (x < 100) {
|
|
2752
|
+
* return Colors.BLUE;
|
|
2753
|
+
* }
|
|
2754
|
+
* });
|
|
2755
|
+
* ```
|
|
2756
|
+
*
|
|
2757
|
+
* For grayscale mode, functions passed to `addRule` should return a number instead of a {@linkcode Colors | pixel-like object}.
|
|
2758
|
+
*
|
|
2759
|
+
* In asynchronous mode, functions should use the {@linkcode set} method to update either this cell
|
|
2760
|
+
* or a different cell.
|
|
2761
|
+
*
|
|
2762
|
+
* ```js
|
|
2763
|
+
* // swaps the colors of this cell and the one five cells to the right
|
|
2764
|
+
* terrain.addRule((x, y) => {
|
|
2765
|
+
* const here = terrain.sample(x, y);
|
|
2766
|
+
* const there = terrain.sample(x + 5, y);
|
|
2767
|
+
* terrain.set(x, y, there);
|
|
2768
|
+
* terrain.set(x + 5, y, here);
|
|
2769
|
+
* });
|
|
2770
|
+
* ```
|
|
2771
|
+
*
|
|
2258
2772
|
* @since 0.4.0
|
|
2259
2773
|
*/
|
|
2260
2774
|
Terrain.prototype.addRule = function (rule) {
|
|
@@ -2262,14 +2776,23 @@
|
|
|
2262
2776
|
};
|
|
2263
2777
|
/**
|
|
2264
2778
|
* Given a local path or remote URL to an image, load that image and set
|
|
2265
|
-
*
|
|
2266
|
-
*
|
|
2779
|
+
* `Terrain` data accordingly. This will scale the image to match the
|
|
2780
|
+
* dimensions of the terrain.
|
|
2781
|
+
*
|
|
2267
2782
|
* A 2nd callback parameter fires once the image has been successfully loaded.
|
|
2268
|
-
*
|
|
2269
|
-
*
|
|
2783
|
+
*
|
|
2784
|
+
* ```js
|
|
2785
|
+
* const terrain = new Terrain(400, 400);
|
|
2786
|
+
* terrain.load("/path/to/local/image.jpg", function() {
|
|
2787
|
+
* console.log("Image loaded successfully!");
|
|
2788
|
+
* });
|
|
2789
|
+
* ```
|
|
2790
|
+
*
|
|
2791
|
+
* @param {string} path - The path to or URL of the image
|
|
2792
|
+
* @param {Function} cb - The function to call once the image loads (takes no parameters)
|
|
2270
2793
|
* @since 0.4.0
|
|
2271
2794
|
*/
|
|
2272
|
-
Terrain.prototype.load = function (path,
|
|
2795
|
+
Terrain.prototype.load = function (path, callback) {
|
|
2273
2796
|
var _this = this;
|
|
2274
2797
|
var img = document.createElement("img");
|
|
2275
2798
|
img.src = path;
|
|
@@ -2283,8 +2806,8 @@
|
|
|
2283
2806
|
.getContext("2d")
|
|
2284
2807
|
.getImageData(0, 0, _this.width, _this.height).data;
|
|
2285
2808
|
_this.data = data;
|
|
2286
|
-
if (
|
|
2287
|
-
|
|
2809
|
+
if (callback)
|
|
2810
|
+
callback();
|
|
2288
2811
|
};
|
|
2289
2812
|
img.onerror = function () {
|
|
2290
2813
|
console.error("There was an error loading the image for the terrain. Check the path to the URL to make sure that it exists, \n or consider saving a local copy to pull from the same origin: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS/Errors");
|
|
@@ -2325,16 +2848,21 @@
|
|
|
2325
2848
|
}
|
|
2326
2849
|
};
|
|
2327
2850
|
/**
|
|
2328
|
-
* Get the neighbors of a
|
|
2329
|
-
* Depending on the fourth parameter, retrieves either the von Neumann neighborhood
|
|
2330
|
-
*
|
|
2331
|
-
* (https://en.wikipedia.org/wiki/Moore_neighborhood).
|
|
2851
|
+
* Get the values of the neighbors of a cell within a certain radius.
|
|
2852
|
+
* Depending on the fourth parameter, retrieves either the {@link https://en.wikipedia.org/wiki/Von_Neumann_neighborhood | von Neumann neighborhood}
|
|
2853
|
+
* or the {@link https://en.wikipedia.org/wiki/Moore_neighborhood | Moore neighborhood}.
|
|
2332
2854
|
*
|
|
2333
|
-
*
|
|
2334
|
-
*
|
|
2335
|
-
*
|
|
2336
|
-
*
|
|
2337
|
-
*
|
|
2855
|
+
* ```js
|
|
2856
|
+
* // in grayscale mode:
|
|
2857
|
+
* terrain.neighbors(5, 5, 1); // returns [127, 100, 255, 255] (4 values)
|
|
2858
|
+
*
|
|
2859
|
+
* // in color mode:
|
|
2860
|
+
* terrain.neighbors(5, 5, 1, true);
|
|
2861
|
+
* // returns [{ r: 255, g: 0, b: 0, a: 255 }, { r: 127, ... }, ...] (8 values)
|
|
2862
|
+
* ```
|
|
2863
|
+
*
|
|
2864
|
+
* @param moore - Defaults to using the von Neumann neighborhood.
|
|
2865
|
+
* @returns Either an array of numbers (grayscale mode) or {@link Colors | pixel-like objects} (color mode).
|
|
2338
2866
|
* @since 0.4.0
|
|
2339
2867
|
*/
|
|
2340
2868
|
Terrain.prototype.neighbors = function (x, y, radius, moore) {
|
|
@@ -2356,6 +2884,7 @@
|
|
|
2356
2884
|
}
|
|
2357
2885
|
return neighbors;
|
|
2358
2886
|
};
|
|
2887
|
+
/** @hidden */
|
|
2359
2888
|
Terrain.prototype._setAbstract = function (data, x, y, r, g, b, a) {
|
|
2360
2889
|
var _a = this, width = _a.width, height = _a.height, opts = _a.opts;
|
|
2361
2890
|
var grayscale = opts.grayscale, scale = opts.scale;
|
|
@@ -2408,9 +2937,11 @@
|
|
|
2408
2937
|
Terrain.prototype.set = function (x, y, r, g, b, a) {
|
|
2409
2938
|
this._setAbstract(this.data, x, y, r, g, b, a);
|
|
2410
2939
|
};
|
|
2940
|
+
/** @hidden */
|
|
2411
2941
|
Terrain.prototype._setNext = function (x, y, r, g, b, a) {
|
|
2412
2942
|
this._setAbstract(this.nextData, x, y, r, g, b, a);
|
|
2413
2943
|
};
|
|
2944
|
+
/** @hidden */
|
|
2414
2945
|
Terrain.prototype._execute = function (x, y) {
|
|
2415
2946
|
var _a = this, rule = _a.rule, opts = _a.opts;
|
|
2416
2947
|
var async = opts.async;
|
|
@@ -2423,6 +2954,7 @@
|
|
|
2423
2954
|
// update on nextData
|
|
2424
2955
|
this._setNext(x, y, result);
|
|
2425
2956
|
};
|
|
2957
|
+
/** @hidden */
|
|
2426
2958
|
Terrain.prototype._loop = function (_a) {
|
|
2427
2959
|
var _b = _a.randomizeOrder, randomizeOrder = _b === void 0 ? false : _b;
|
|
2428
2960
|
var _c = this, rule = _c.rule, width = _c.width, height = _c.height, opts = _c.opts;
|
|
@@ -2452,52 +2984,272 @@
|
|
|
2452
2984
|
return Terrain;
|
|
2453
2985
|
}());
|
|
2454
2986
|
|
|
2455
|
-
var defaultTickOptions = {
|
|
2456
|
-
activation: "uniform",
|
|
2457
|
-
count: 1,
|
|
2458
|
-
randomizeOrder: false
|
|
2459
|
-
};
|
|
2460
|
-
var defaultEnvironmentOptions = {
|
|
2461
|
-
torus: true,
|
|
2462
|
-
height: 0,
|
|
2463
|
-
width: 0
|
|
2464
|
-
};
|
|
2465
|
-
var warnOnce = once(console.warn.bind(console));
|
|
2466
2987
|
/**
|
|
2467
|
-
*
|
|
2468
|
-
*
|
|
2469
|
-
*
|
|
2988
|
+
* Given a `number` and `min` and `max` values, restrict the number
|
|
2989
|
+
* to the range specified.
|
|
2990
|
+
*
|
|
2991
|
+
* ```js
|
|
2992
|
+
* clamp(5, 1, 10); // returns 5
|
|
2993
|
+
* clamp(5, 2, 4); // returns 4
|
|
2994
|
+
* clamp(0, -4, -3); // returns -3
|
|
2995
|
+
* ```
|
|
2996
|
+
*
|
|
2470
2997
|
* @since 0.0.5
|
|
2471
2998
|
*/
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2999
|
+
function clamp(x, min, max) {
|
|
3000
|
+
if (x < min)
|
|
3001
|
+
return min;
|
|
3002
|
+
if (x > max)
|
|
3003
|
+
return max;
|
|
3004
|
+
return x;
|
|
3005
|
+
}
|
|
3006
|
+
|
|
3007
|
+
/**
|
|
3008
|
+
* Given a mean and standard deviation,
|
|
3009
|
+
* returns a value from a normal/Gaussian distribution.
|
|
3010
|
+
*
|
|
3011
|
+
* ```js
|
|
3012
|
+
* // returns values mostly between 5 and 15 (but sometimes lower or higher)
|
|
3013
|
+
* gaussian(10, 5);
|
|
3014
|
+
*
|
|
3015
|
+
* // no parameters defaults to mean = 0, std. dev. = 1
|
|
3016
|
+
* gaussian(); // mostly values between -1 and 1
|
|
3017
|
+
* ```
|
|
3018
|
+
*
|
|
3019
|
+
* @since 0.0.8
|
|
3020
|
+
*/
|
|
3021
|
+
function gaussian(mean, sd) {
|
|
3022
|
+
if (mean === void 0) { mean = 0; }
|
|
3023
|
+
if (sd === void 0) { sd = 1; }
|
|
3024
|
+
var y, x1, x2, w;
|
|
3025
|
+
do {
|
|
3026
|
+
x1 = 2 * uniform() - 1;
|
|
3027
|
+
x2 = 2 * uniform() - 1;
|
|
3028
|
+
w = x1 * x1 + x2 * x2;
|
|
3029
|
+
} while (w >= 1);
|
|
3030
|
+
w = Math.sqrt((-2 * Math.log(w)) / w);
|
|
3031
|
+
y = x1 * w;
|
|
3032
|
+
return y * sd + mean;
|
|
3033
|
+
}
|
|
3034
|
+
|
|
3035
|
+
/// <reference path="../types/Point.d.ts" />
|
|
3036
|
+
/**
|
|
3037
|
+
* Finds the {@link https://en.wikipedia.org/wiki/Taxicab_geometry | Manhattan distance} between `p1` and `p2`.
|
|
3038
|
+
*
|
|
3039
|
+
* The inputs may be plain objects with `x`, `y`, and/or `z` keys, {@linkcode Vector}s,
|
|
3040
|
+
* or {@linkcode Agent}s with `x`, `y`, and/or `z` data.
|
|
3041
|
+
*
|
|
3042
|
+
* ```js
|
|
3043
|
+
* const a1 = new Agent();
|
|
3044
|
+
* const a2 = new Agent({ x: 3, y: 4 });
|
|
3045
|
+
* manhattanDistance(a1, a2); // returns 7 (defaults to x = 0 and y = 0 for a1)
|
|
3046
|
+
*
|
|
3047
|
+
* const p1 = { x: 3, y: 2 };
|
|
3048
|
+
* const p2 = { x: 0, y: 4 };
|
|
3049
|
+
* manhattanDistance(p1, p2); // returns 5
|
|
3050
|
+
* ```
|
|
3051
|
+
*
|
|
3052
|
+
* @since 0.0.12
|
|
3053
|
+
*/
|
|
3054
|
+
function manhattanDistance(p1, p2) {
|
|
3055
|
+
var x1 = (p1 instanceof Agent ? p1.get("x") : p1.x) || 0;
|
|
3056
|
+
var y1 = (p1 instanceof Agent ? p1.get("y") : p1.y) || 0;
|
|
3057
|
+
var z1 = (p1 instanceof Agent ? p1.get("z") : p1.z) || 0;
|
|
3058
|
+
var x2 = (p2 instanceof Agent ? p2.get("x") : p2.x) || 0;
|
|
3059
|
+
var y2 = (p2 instanceof Agent ? p2.get("y") : p2.y) || 0;
|
|
3060
|
+
var z2 = (p2 instanceof Agent ? p2.get("z") : p2.z) || 0;
|
|
3061
|
+
var dx = Math.abs(x2 - x1);
|
|
3062
|
+
var dy = Math.abs(y2 - y1);
|
|
3063
|
+
var dz = Math.abs(z2 - z1);
|
|
3064
|
+
// distance for toroidal environments
|
|
3065
|
+
if (p1 instanceof Agent &&
|
|
3066
|
+
p2 instanceof Agent &&
|
|
3067
|
+
p1.environment &&
|
|
3068
|
+
p2.environment &&
|
|
3069
|
+
p1.environment === p2.environment &&
|
|
3070
|
+
p1.environment.width &&
|
|
3071
|
+
p1.environment.height &&
|
|
3072
|
+
p1.environment.opts.torus) {
|
|
3073
|
+
var environment = p1.environment;
|
|
3074
|
+
var width = environment.width, height = environment.height;
|
|
3075
|
+
if (dx > width / 2)
|
|
3076
|
+
dx = width - dx;
|
|
3077
|
+
if (dy > height / 2)
|
|
3078
|
+
dy = height - dy;
|
|
2495
3079
|
}
|
|
3080
|
+
return dx + dy + dz;
|
|
3081
|
+
}
|
|
3082
|
+
|
|
3083
|
+
/**
|
|
3084
|
+
* Maps a number x, from the given domain aMin --> aMax,
|
|
3085
|
+
* onto the given range bMin --> bMax.
|
|
3086
|
+
* Ex: remap(5, 0, 10, 0, 100) => 50.
|
|
3087
|
+
* @param {number} x
|
|
3088
|
+
* @param {number} aMin
|
|
3089
|
+
* @param {number} aMax
|
|
3090
|
+
* @param {number} bMin
|
|
3091
|
+
* @param {number} bMax
|
|
3092
|
+
* @returns {number} The remapped value.
|
|
3093
|
+
* @since 0.0.5
|
|
3094
|
+
*/
|
|
3095
|
+
function remap(x, aMin, aMax, bMin, bMax) {
|
|
3096
|
+
return bMin + ((bMax - bMin) * (x - aMin)) / (aMax - aMin);
|
|
3097
|
+
}
|
|
3098
|
+
|
|
3099
|
+
/**
|
|
3100
|
+
* Seed a pseudo-random number generator with a value.
|
|
3101
|
+
* This can be used to produce predictable pseudo-random numbers.
|
|
3102
|
+
* When calling `utils.random`, `utils.sample`, or other functions
|
|
3103
|
+
* relying on randomness with the same initial seed, the values
|
|
3104
|
+
* generated will always be the same.
|
|
3105
|
+
*
|
|
3106
|
+
* Predictable randomness can be turned off by calling `seed(null)`, or reset
|
|
3107
|
+
* by calling `seed(value)` again with the initial value you used.
|
|
3108
|
+
* @param value
|
|
3109
|
+
* @since 0.5.0
|
|
3110
|
+
*/
|
|
3111
|
+
var seed = function (value) { return PRNG.seed(value); };
|
|
3112
|
+
|
|
3113
|
+
/**
|
|
3114
|
+
* Find the standard deviation of an Array of numbers.
|
|
3115
|
+
* @param {Array<number>} arr
|
|
3116
|
+
* @returns {number}
|
|
3117
|
+
* @since 0.0.16
|
|
3118
|
+
*/
|
|
3119
|
+
function stdDev(arr) {
|
|
3120
|
+
if (arr.length === 0)
|
|
3121
|
+
return null;
|
|
3122
|
+
var ave = mean(arr);
|
|
3123
|
+
return Math.sqrt(mean(arr.map(function (x) { return (x - ave) * (x - ave); })));
|
|
3124
|
+
}
|
|
3125
|
+
|
|
3126
|
+
/**
|
|
3127
|
+
* @since 0.1.4
|
|
3128
|
+
*/
|
|
3129
|
+
function zfill(str, width) {
|
|
3130
|
+
if (width === void 0) { width = 0; }
|
|
3131
|
+
var output = str;
|
|
3132
|
+
while (output.length < width)
|
|
3133
|
+
output = "0" + output;
|
|
3134
|
+
return output;
|
|
3135
|
+
}
|
|
3136
|
+
|
|
3137
|
+
|
|
3138
|
+
|
|
3139
|
+
var utils = /*#__PURE__*/Object.freeze({
|
|
3140
|
+
__proto__: null,
|
|
3141
|
+
clamp: clamp,
|
|
3142
|
+
distance: distance,
|
|
3143
|
+
gaussian: gaussian,
|
|
3144
|
+
gcd: gcd,
|
|
3145
|
+
manhattanDistance: manhattanDistance,
|
|
3146
|
+
lerp: lerp,
|
|
3147
|
+
remap: remap,
|
|
3148
|
+
random: random,
|
|
3149
|
+
sample: sample$1,
|
|
3150
|
+
sampler: sampler,
|
|
3151
|
+
seed: seed,
|
|
3152
|
+
series: series,
|
|
3153
|
+
shuffle: shuffle,
|
|
3154
|
+
sum: sum,
|
|
3155
|
+
max: max,
|
|
3156
|
+
mean: mean,
|
|
3157
|
+
median: median,
|
|
3158
|
+
min: min,
|
|
3159
|
+
percentile: percentile,
|
|
3160
|
+
stdDev: stdDev,
|
|
3161
|
+
uniform: uniform,
|
|
3162
|
+
uuid: uuid$1,
|
|
3163
|
+
zfill: zfill
|
|
3164
|
+
});
|
|
3165
|
+
|
|
3166
|
+
var defaultTickOptions = {
|
|
3167
|
+
activation: "uniform",
|
|
3168
|
+
activationCount: 1,
|
|
3169
|
+
count: 1,
|
|
3170
|
+
randomizeOrder: false
|
|
3171
|
+
};
|
|
3172
|
+
var defaultEnvironmentOptions = {
|
|
3173
|
+
torus: true,
|
|
3174
|
+
height: 0,
|
|
3175
|
+
width: 0
|
|
3176
|
+
};
|
|
3177
|
+
var warnOnce = once(console.warn.bind(console));
|
|
3178
|
+
/**
|
|
3179
|
+
* An environment provides the space and time in which Agents interact.
|
|
3180
|
+
* Environments are themselves Agents, and can store data in key-value
|
|
3181
|
+
* pairs that can be manipulated just like Agent data.
|
|
3182
|
+
* @since 0.0.5
|
|
3183
|
+
*/
|
|
3184
|
+
var Environment = /** @class */ (function (_super) {
|
|
3185
|
+
__extends(Environment, _super);
|
|
2496
3186
|
/**
|
|
2497
|
-
*
|
|
2498
|
-
*
|
|
2499
|
-
*
|
|
2500
|
-
*
|
|
3187
|
+
* Although `Environment`s inherit {@linkcode Agent} methods
|
|
3188
|
+
* like {@linkcode Agent.set}, {@linkcode Agent.get}, etc. they have
|
|
3189
|
+
* a different `constructor` signature.
|
|
3190
|
+
*
|
|
3191
|
+
* Pass in predefined `Environment` options for:
|
|
3192
|
+
* - `torus` — Whether the `Environment` should wrap around in 2d space (with `Agent`s that move off the right reappearing on the left, off the top reappearing on the bottom, etc.)
|
|
3193
|
+
* - `width` — The width of the `Environment` (used when `torus = true`)
|
|
3194
|
+
* - `height` — The height of the `Environment` (used when `torus = true`)
|
|
3195
|
+
* @override
|
|
3196
|
+
*/
|
|
3197
|
+
function Environment(opts) {
|
|
3198
|
+
if (opts === void 0) { opts = defaultEnvironmentOptions; }
|
|
3199
|
+
var _this = _super.call(this) || this;
|
|
3200
|
+
/** @hidden */
|
|
3201
|
+
_this.agents = [];
|
|
3202
|
+
/** @hidden */
|
|
3203
|
+
_this.agentsById = new Map();
|
|
3204
|
+
/** @hidden */
|
|
3205
|
+
_this.environment = null;
|
|
3206
|
+
/** @hidden */
|
|
3207
|
+
_this.cache = new Map();
|
|
3208
|
+
/** @hidden */
|
|
3209
|
+
_this.helpers = {
|
|
3210
|
+
kdtree: null,
|
|
3211
|
+
network: null,
|
|
3212
|
+
terrain: null
|
|
3213
|
+
};
|
|
3214
|
+
/**
|
|
3215
|
+
* An array of the renderers associated with this `Environment`.
|
|
3216
|
+
* An `Environment` can have multiple renderers, usually one to render
|
|
3217
|
+
* the {@linkcode Agent}s spatially and others for data visualization,
|
|
3218
|
+
* such as a {@linkcode LineChartRenderer}, {@linkcode Histogram}, etc.
|
|
3219
|
+
*/
|
|
3220
|
+
_this.renderers = [];
|
|
3221
|
+
/**
|
|
3222
|
+
* This property will always equal the number of tick cycles that
|
|
3223
|
+
* have passed since the `Environment` was created. If you call
|
|
3224
|
+
* {@linkcode tick} so that it goes forward multiple time steps, it will
|
|
3225
|
+
* increase the `time` by that value (not by just `1`, even though
|
|
3226
|
+
* you only called `tick` once).
|
|
3227
|
+
*
|
|
3228
|
+
* ```js
|
|
3229
|
+
* const environment = new Environment();
|
|
3230
|
+
* environment.time; // returns 0
|
|
3231
|
+
*
|
|
3232
|
+
* environment.tick();
|
|
3233
|
+
* environment.time; // returns 1
|
|
3234
|
+
*
|
|
3235
|
+
* environment.tick(3);
|
|
3236
|
+
* environment.time; // returns 4
|
|
3237
|
+
* ```
|
|
3238
|
+
*
|
|
3239
|
+
* @since 0.1.4
|
|
3240
|
+
* */
|
|
3241
|
+
_this.time = 0;
|
|
3242
|
+
_this.opts = Object.assign({}, defaultEnvironmentOptions);
|
|
3243
|
+
_this.opts = Object.assign(_this.opts, opts);
|
|
3244
|
+
_this.width = _this.opts.width;
|
|
3245
|
+
_this.height = _this.opts.height;
|
|
3246
|
+
return _this;
|
|
3247
|
+
}
|
|
3248
|
+
/**
|
|
3249
|
+
* Add an {@linkcode Agent} to this `Environment`. Once this is called,
|
|
3250
|
+
* the `Agent`'s {@link Agent.environment | `environment`} property
|
|
3251
|
+
* will automatically be set to this `Environment`.
|
|
3252
|
+
* @param rebalance - Whether to rebalance if there is a `KDTree` (defaults to true)
|
|
2501
3253
|
* @since 0.0.5
|
|
2502
3254
|
*/
|
|
2503
3255
|
Environment.prototype.addAgent = function (agent, rebalance) {
|
|
@@ -2559,7 +3311,7 @@
|
|
|
2559
3311
|
return this.agentsById.get(id) || null;
|
|
2560
3312
|
};
|
|
2561
3313
|
/**
|
|
2562
|
-
*
|
|
3314
|
+
* Remove all agents from the environment.
|
|
2563
3315
|
* @since 0.1.3
|
|
2564
3316
|
*/
|
|
2565
3317
|
Environment.prototype.clear = function () {
|
|
@@ -2569,64 +3321,132 @@
|
|
|
2569
3321
|
}
|
|
2570
3322
|
};
|
|
2571
3323
|
/**
|
|
2572
|
-
* From the parameter passed to .tick, get a structured TickOptions object.
|
|
2573
|
-
* @
|
|
3324
|
+
* From the parameter passed to {@linkcode Environment.tick}, get a structured TickOptions object.
|
|
3325
|
+
* @hidden
|
|
2574
3326
|
*/
|
|
2575
3327
|
Environment.prototype._getTickOptions = function (opts) {
|
|
2576
|
-
var
|
|
3328
|
+
var baseOpts = Object.assign({}, defaultTickOptions);
|
|
2577
3329
|
if (typeof opts === "number") {
|
|
2578
|
-
count = opts;
|
|
3330
|
+
baseOpts.count = opts;
|
|
2579
3331
|
}
|
|
2580
3332
|
else if (!!opts) {
|
|
2581
|
-
|
|
2582
|
-
}
|
|
2583
|
-
if (opts &&
|
|
2584
|
-
typeof opts !== "number" &&
|
|
2585
|
-
opts.hasOwnProperty("randomizeOrder")) {
|
|
2586
|
-
randomizeOrder = opts.randomizeOrder;
|
|
3333
|
+
Object.assign(baseOpts, opts);
|
|
2587
3334
|
}
|
|
2588
|
-
|
|
3335
|
+
if (opts === undefined ||
|
|
2589
3336
|
(typeof opts !== "number" && !opts.hasOwnProperty("randomizeOrder"))) {
|
|
2590
3337
|
warnOnce("You called `environment.tick` without specifying a `randomizeOrder` option. Currently this defaults to `false` (i.e. each agent ticks in the order it was added to the environment). However, in **Flocc 0.6.0 this will default to `true`** — agent activation order will default to being randomized.");
|
|
2591
3338
|
}
|
|
2592
|
-
return
|
|
3339
|
+
return baseOpts;
|
|
2593
3340
|
};
|
|
2594
3341
|
/**
|
|
2595
|
-
*
|
|
2596
|
-
* @
|
|
3342
|
+
* For all agents passed, execute agent rules
|
|
3343
|
+
* @hidden
|
|
2597
3344
|
*/
|
|
2598
|
-
Environment.prototype._executeAgentRules = function (
|
|
2599
|
-
(
|
|
2600
|
-
return agent.executeRules();
|
|
2601
|
-
});
|
|
3345
|
+
Environment.prototype._executeAgentRules = function (agents) {
|
|
3346
|
+
agents.forEach(function (agent) { return agent === null || agent === void 0 ? void 0 : agent.executeRules(); });
|
|
2602
3347
|
};
|
|
2603
3348
|
/**
|
|
2604
|
-
*
|
|
2605
|
-
* @
|
|
3349
|
+
* For all agents passed, execute enqueued agent rules
|
|
3350
|
+
* @hidden
|
|
2606
3351
|
*/
|
|
2607
|
-
Environment.prototype._executeEnqueuedAgentRules = function (
|
|
2608
|
-
(
|
|
2609
|
-
return agent.executeEnqueuedRules();
|
|
2610
|
-
});
|
|
3352
|
+
Environment.prototype._executeEnqueuedAgentRules = function (agents) {
|
|
3353
|
+
agents.forEach(function (agent) { return agent === null || agent === void 0 ? void 0 : agent.executeEnqueuedRules(); });
|
|
2611
3354
|
};
|
|
2612
3355
|
/**
|
|
2613
|
-
*
|
|
2614
|
-
*
|
|
2615
|
-
*
|
|
2616
|
-
*
|
|
2617
|
-
*
|
|
2618
|
-
*
|
|
3356
|
+
* Runs the `Environment`s tick cycle. Depending on the parameters, one,
|
|
3357
|
+
* some, or all of the {@linkcode Agent}s in the `Environment`
|
|
3358
|
+
* might be activated, and all renderers associated with the
|
|
3359
|
+
* `Environment` will update. After the tick cycle finishes, any rules that were enqueued will be run and the `Environment`'s {@linkcode time} property will have incremented.
|
|
3360
|
+
*
|
|
3361
|
+
* ```js
|
|
3362
|
+
* environment.tick(); // ticks once
|
|
3363
|
+
*
|
|
3364
|
+
* // To run multiple tick cycles, you can pass a number
|
|
3365
|
+
* environment.tick(5); // ticks 5 times
|
|
3366
|
+
* ```
|
|
3367
|
+
*
|
|
3368
|
+
* Passing a configuration object (instead of a number) allows
|
|
3369
|
+
* you to have finer control over the tick cycle. The object can
|
|
3370
|
+
* have the following keys:
|
|
3371
|
+
* - `activation`: Either `"uniform"` or `"random"` (defaults to `"uniform"`).
|
|
3372
|
+
* - `activation = "uniform"` — All `Agent`s in the `Environment` are activated with every tick cycle.
|
|
3373
|
+
* - `activation = "random"` — One or more `Agent`s are randomly selected to be activated every tick cycle (see `activationCount` below).
|
|
3374
|
+
* - `activationCount`: For `"random"` activation, this many `Agent`s will be activated with each tick cycle. Defaults to `1`. If `activationCount` is greater than the number of `Agent`s in the `Environment`, then all the `Agent`s will be activated exactly once in random order.
|
|
3375
|
+
* - `count`: The number of tick cycles to run.
|
|
3376
|
+
* - `randomizeOrder`: When `activation = "uniform"`, if `randomizeOrder = true`, `Agent`s will be activated in random order, otherwise in the order they were added to the `Environment`. **This currently defaults to `false` but will default to `true` in v0.6.0.**
|
|
3377
|
+
*
|
|
3378
|
+
* ```js
|
|
3379
|
+
* // Ticks three times, activating 10 random agents with each tick cycle.
|
|
3380
|
+
* environment.tick({
|
|
3381
|
+
* activation: "random",
|
|
3382
|
+
* activationCount: 10,
|
|
3383
|
+
* count: 3
|
|
3384
|
+
* });
|
|
3385
|
+
* ```
|
|
3386
|
+
*
|
|
2619
3387
|
* @since 0.0.5
|
|
2620
3388
|
*/
|
|
2621
3389
|
Environment.prototype.tick = function (opts) {
|
|
2622
|
-
var _a = this._getTickOptions(opts), count = _a.count, randomizeOrder = _a.randomizeOrder;
|
|
2623
|
-
|
|
2624
|
-
|
|
3390
|
+
var _a = this._getTickOptions(opts), activation = _a.activation, activationCount = _a.activationCount, count = _a.count, randomizeOrder = _a.randomizeOrder;
|
|
3391
|
+
// for uniform activation, every agent is always activated
|
|
3392
|
+
if (activation === "uniform") {
|
|
3393
|
+
var agentsInOrder = randomizeOrder ? shuffle(this.agents) : this.agents;
|
|
3394
|
+
this._executeAgentRules(agentsInOrder);
|
|
3395
|
+
this._executeEnqueuedAgentRules(agentsInOrder);
|
|
3396
|
+
}
|
|
3397
|
+
// for random activation, the number of agents activated
|
|
3398
|
+
// per tick is determined by the `activationCount` option
|
|
3399
|
+
else if (activation === "random") {
|
|
3400
|
+
if (activationCount === 1) {
|
|
3401
|
+
var agent = sample$1(this.agents);
|
|
3402
|
+
if (agent !== null) {
|
|
3403
|
+
agent.executeRules();
|
|
3404
|
+
agent.executeEnqueuedRules();
|
|
3405
|
+
}
|
|
3406
|
+
}
|
|
3407
|
+
else if (activationCount > 1) {
|
|
3408
|
+
var sampleCount = sampler(activationCount);
|
|
3409
|
+
// this safety check should always return `true`
|
|
3410
|
+
if (isMultipleSampleFunc(sampleCount)) {
|
|
3411
|
+
var agents = sampleCount(this.getAgents());
|
|
3412
|
+
this._executeAgentRules(agents);
|
|
3413
|
+
this._executeEnqueuedAgentRules(agents);
|
|
3414
|
+
}
|
|
3415
|
+
}
|
|
3416
|
+
else {
|
|
3417
|
+
warnOnce("You passed a zero or negative `activationCount` to the Environment's tick options. No agents will be activated.");
|
|
3418
|
+
}
|
|
3419
|
+
}
|
|
2625
3420
|
if (this.helpers.kdtree)
|
|
2626
3421
|
this.helpers.kdtree.rebalance();
|
|
2627
3422
|
var terrain = this.helpers.terrain;
|
|
2628
|
-
if (terrain && terrain.rule)
|
|
2629
|
-
|
|
3423
|
+
if (terrain && terrain.rule) {
|
|
3424
|
+
if (activation === "uniform") {
|
|
3425
|
+
terrain._loop({ randomizeOrder: randomizeOrder });
|
|
3426
|
+
}
|
|
3427
|
+
else if (activation === "random") {
|
|
3428
|
+
if (activationCount === 1) {
|
|
3429
|
+
var x = random(0, terrain.width);
|
|
3430
|
+
var y = random(0, terrain.height);
|
|
3431
|
+
terrain._execute(x, y);
|
|
3432
|
+
}
|
|
3433
|
+
else if (activationCount > 1) {
|
|
3434
|
+
var generator = series(terrain.width * terrain.height);
|
|
3435
|
+
var indices = [];
|
|
3436
|
+
while (indices.length < activationCount) {
|
|
3437
|
+
var index = generator.next().value;
|
|
3438
|
+
var x = index % terrain.width;
|
|
3439
|
+
var y = (index / terrain.width) | 0;
|
|
3440
|
+
terrain._execute(x, y);
|
|
3441
|
+
indices.push(index);
|
|
3442
|
+
}
|
|
3443
|
+
}
|
|
3444
|
+
// in synchronous mode, write the buffer to the data
|
|
3445
|
+
if (!terrain.opts.async) {
|
|
3446
|
+
terrain.data = new Uint8ClampedArray(terrain.nextData);
|
|
3447
|
+
}
|
|
3448
|
+
}
|
|
3449
|
+
}
|
|
2630
3450
|
this.time++;
|
|
2631
3451
|
if (count > 1) {
|
|
2632
3452
|
this.tick(count - 1);
|
|
@@ -2635,26 +3455,41 @@
|
|
|
2635
3455
|
this.renderers.forEach(function (r) { return r.render(); });
|
|
2636
3456
|
};
|
|
2637
3457
|
/**
|
|
2638
|
-
* Use a helper with this environment.
|
|
2639
|
-
* @
|
|
3458
|
+
* Use a helper with this environment. A helper can be one of:
|
|
3459
|
+
* - {@linkcode KDTree}
|
|
3460
|
+
* - {@linkcode Network}
|
|
3461
|
+
* - {@linkcode Terrain}
|
|
2640
3462
|
* @since 0.1.3
|
|
2641
3463
|
*/
|
|
2642
|
-
Environment.prototype.use = function (
|
|
2643
|
-
if (
|
|
2644
|
-
this.helpers.kdtree =
|
|
2645
|
-
if (
|
|
2646
|
-
this.helpers.network =
|
|
2647
|
-
if (
|
|
2648
|
-
this.helpers.terrain =
|
|
3464
|
+
Environment.prototype.use = function (helper) {
|
|
3465
|
+
if (helper instanceof KDTree)
|
|
3466
|
+
this.helpers.kdtree = helper;
|
|
3467
|
+
if (helper instanceof Network)
|
|
3468
|
+
this.helpers.network = helper;
|
|
3469
|
+
if (helper instanceof Terrain)
|
|
3470
|
+
this.helpers.terrain = helper;
|
|
2649
3471
|
};
|
|
2650
3472
|
/**
|
|
2651
3473
|
* Get an array of data associated with agents in the environment by key.
|
|
2652
|
-
*
|
|
2653
|
-
*
|
|
2654
|
-
*
|
|
2655
|
-
*
|
|
2656
|
-
*
|
|
2657
|
-
*
|
|
3474
|
+
* Calling `environment.stat('name')` is equivalent to calling
|
|
3475
|
+
* `environment.getAgents().map(agent => agent.get('name'));`
|
|
3476
|
+
*
|
|
3477
|
+
* By default, calling this will calculate the result at most once
|
|
3478
|
+
* per time cycle, and return the cached value on subsequent calls (until
|
|
3479
|
+
* the next time cycle, when it will recalculate).
|
|
3480
|
+
*
|
|
3481
|
+
* @param key - The key for which to retrieve data.
|
|
3482
|
+
* @param useCache - Whether or not to cache the result.
|
|
3483
|
+
* @returns Array of data associated with `agent.get(key)` across all agents.
|
|
3484
|
+
*
|
|
3485
|
+
* ```js
|
|
3486
|
+
* environment.addAgent(new Agent({ name: "Alice" }));
|
|
3487
|
+
* environment.addAgent(new Agent({ name: "Bob" }));
|
|
3488
|
+
* environment.addAgent(new Agent({ name: "Chaz" }));
|
|
3489
|
+
*
|
|
3490
|
+
* environment.stat('name'); // returns ['Alice', 'Bob', 'Chaz']
|
|
3491
|
+
* ```
|
|
3492
|
+
*
|
|
2658
3493
|
* @since 0.3.14
|
|
2659
3494
|
*/
|
|
2660
3495
|
Environment.prototype.stat = function (key, useCache) {
|
|
@@ -2675,9 +3510,17 @@
|
|
|
2675
3510
|
};
|
|
2676
3511
|
/**
|
|
2677
3512
|
* Pass a function to cache and use the return value within the same environment tick.
|
|
2678
|
-
* @param
|
|
2679
|
-
* @return
|
|
3513
|
+
* @param fn - The function to memoize.
|
|
3514
|
+
* @return The return value of the function that was passed.
|
|
2680
3515
|
* @since 0.3.14
|
|
3516
|
+
*
|
|
3517
|
+
* ```js
|
|
3518
|
+
* // Within the same time cycle, this function will only be called once.
|
|
3519
|
+
* // The cached value will be used on subsequent calls.
|
|
3520
|
+
* const blueAgents = environment.memo(() => {
|
|
3521
|
+
* return environment.getAgents().filter(a => a.get('color') === 'blue');
|
|
3522
|
+
* });
|
|
3523
|
+
* ```
|
|
2681
3524
|
*/
|
|
2682
3525
|
Environment.prototype.memo = function (fn, key) {
|
|
2683
3526
|
var serialized = (key ? key + "-" : "") + fn.toString();
|
|
@@ -2718,10 +3561,21 @@
|
|
|
2718
3561
|
};
|
|
2719
3562
|
var warnOnce$1 = once(console.warn.bind(console));
|
|
2720
3563
|
/**
|
|
2721
|
-
*
|
|
3564
|
+
* A `GridEnvironment` is the **deprecated** version of a cellular automata.
|
|
3565
|
+
* It's now recommended that you use a standard {@linkcode Environment} with a
|
|
3566
|
+
* {@linkcode Terrain}. This class will be removed entirely in v0.6.0.
|
|
3567
|
+
*
|
|
3568
|
+
* In a `GridEnvironment` with an {@linkcode ASCIIRenderer}, {@linkcode Agent}s are rendered
|
|
3569
|
+
* using their `"value"` data (a single character).
|
|
3570
|
+
*
|
|
3571
|
+
* @deprecated since 0.4.0
|
|
3572
|
+
* @since 0.0.10
|
|
2722
3573
|
*/
|
|
2723
3574
|
var GridEnvironment = /** @class */ (function (_super) {
|
|
2724
3575
|
__extends(GridEnvironment, _super);
|
|
3576
|
+
/**
|
|
3577
|
+
* Create a `GridEnvironment` with the given `width` and `height`.
|
|
3578
|
+
*/
|
|
2725
3579
|
function GridEnvironment(width, height) {
|
|
2726
3580
|
if (width === void 0) { width = 2; }
|
|
2727
3581
|
if (height === void 0) { height = 2; }
|
|
@@ -2756,6 +3610,7 @@
|
|
|
2756
3610
|
}
|
|
2757
3611
|
};
|
|
2758
3612
|
/**
|
|
3613
|
+
* @hidden
|
|
2759
3614
|
* @since 0.1.0
|
|
2760
3615
|
*/
|
|
2761
3616
|
GridEnvironment.prototype.normalize = function (x, y) {
|
|
@@ -2770,7 +3625,7 @@
|
|
|
2770
3625
|
return { x: x, y: y };
|
|
2771
3626
|
};
|
|
2772
3627
|
/**
|
|
2773
|
-
* For
|
|
3628
|
+
* For `GridEnvironment`s, `addAgent` takes `x` and `y` values
|
|
2774
3629
|
* and automatically adds a Agent to that cell coordinate.
|
|
2775
3630
|
* @param {number} x_
|
|
2776
3631
|
* @param {number} y_
|
|
@@ -2799,8 +3654,8 @@
|
|
|
2799
3654
|
return agent;
|
|
2800
3655
|
};
|
|
2801
3656
|
/**
|
|
2802
|
-
* For GridEnvironments, `
|
|
2803
|
-
* and removes the Agent (if there is one) at that cell coordinate.
|
|
3657
|
+
* For GridEnvironments, `removeAgentAt` takes `x` and `y` values
|
|
3658
|
+
* and removes the `Agent` (if there is one) at that cell coordinate.
|
|
2804
3659
|
* @param {number} x_
|
|
2805
3660
|
* @param {number} y_
|
|
2806
3661
|
* @since 0.1.0
|
|
@@ -2963,6 +3818,7 @@
|
|
|
2963
3818
|
};
|
|
2964
3819
|
/**
|
|
2965
3820
|
* Execute all cell rules.
|
|
3821
|
+
* @hidden
|
|
2966
3822
|
* @param { boolean } randomizeOrder
|
|
2967
3823
|
*/
|
|
2968
3824
|
GridEnvironment.prototype._executeCellRules = function (randomizeOrder) {
|
|
@@ -2989,6 +3845,7 @@
|
|
|
2989
3845
|
};
|
|
2990
3846
|
/**
|
|
2991
3847
|
* Execute all enqueued cell rules.
|
|
3848
|
+
* @hidden
|
|
2992
3849
|
* @param { boolean } randomizeOrder
|
|
2993
3850
|
*/
|
|
2994
3851
|
GridEnvironment.prototype._executeEnqueuedCellRules = function (randomizeOrder) {
|
|
@@ -3014,8 +3871,8 @@
|
|
|
3014
3871
|
}
|
|
3015
3872
|
};
|
|
3016
3873
|
/**
|
|
3017
|
-
* Override/extend Environment.tick to include the
|
|
3018
|
-
* GridEnvironment's cells.
|
|
3874
|
+
* Override/extend {@linkcode Environment.tick} to include the
|
|
3875
|
+
* `GridEnvironment`'s cells.
|
|
3019
3876
|
* @override
|
|
3020
3877
|
* @param {number} opts
|
|
3021
3878
|
*/
|
|
@@ -3024,11 +3881,11 @@
|
|
|
3024
3881
|
// execute all cell rules
|
|
3025
3882
|
this._executeCellRules(randomizeOrder);
|
|
3026
3883
|
// execute all agent rules
|
|
3027
|
-
this._executeAgentRules(randomizeOrder);
|
|
3884
|
+
this._executeAgentRules(randomizeOrder ? shuffle(this.agents) : this.agents);
|
|
3028
3885
|
// execute all enqueued cell rules
|
|
3029
3886
|
this._executeEnqueuedCellRules(randomizeOrder);
|
|
3030
3887
|
// execute all enqueued agent rules
|
|
3031
|
-
this._executeEnqueuedAgentRules(randomizeOrder);
|
|
3888
|
+
this._executeEnqueuedAgentRules(randomizeOrder ? shuffle(this.agents) : this.agents);
|
|
3032
3889
|
this.time++;
|
|
3033
3890
|
if (count > 1) {
|
|
3034
3891
|
this.tick(count - 1);
|
|
@@ -3041,13 +3898,24 @@
|
|
|
3041
3898
|
|
|
3042
3899
|
var AbstractRenderer = /** @class */ (function () {
|
|
3043
3900
|
function AbstractRenderer() {
|
|
3901
|
+
/** @hidden */
|
|
3044
3902
|
this.canvas = document.createElement("canvas");
|
|
3903
|
+
/** @hidden */
|
|
3045
3904
|
this.context = this.canvas.getContext("2d");
|
|
3046
3905
|
}
|
|
3047
3906
|
AbstractRenderer.prototype.render = function () { };
|
|
3048
3907
|
/**
|
|
3049
3908
|
* Mount this renderer to a DOM element. Pass either a string representing a
|
|
3050
|
-
* CSS selector matching the element
|
|
3909
|
+
* CSS selector matching the element or the element itself.
|
|
3910
|
+
*
|
|
3911
|
+
* ```js
|
|
3912
|
+
* // mounts the renderer to the element with the ID `container`
|
|
3913
|
+
* renderer.mount('#container');
|
|
3914
|
+
*
|
|
3915
|
+
* // mounts the renderer to the element itself
|
|
3916
|
+
* const container = document.getElementById('container');
|
|
3917
|
+
* renderer.mount(container);
|
|
3918
|
+
* ```
|
|
3051
3919
|
* @param {string | HTMLElement} el
|
|
3052
3920
|
*/
|
|
3053
3921
|
AbstractRenderer.prototype.mount = function (el) {
|
|
@@ -3062,11 +3930,25 @@
|
|
|
3062
3930
|
|
|
3063
3931
|
var warnOnce$2 = once(console.warn.bind(console));
|
|
3064
3932
|
/**
|
|
3933
|
+
* An `ASCIIRenderer` renders the {@link Agent | `Agent`}s in
|
|
3934
|
+
* a {@linkcode GridEnvironment}. `Agent`s are rendered
|
|
3935
|
+
* using their `"value"` data (a single character).
|
|
3936
|
+
* Since v0.4.0, this class has been **deprecated**, and it will be removed
|
|
3937
|
+
* entirely in v0.6.0.
|
|
3938
|
+
* ```js
|
|
3939
|
+
* const renderer = new ASCIIRenderer(grid);
|
|
3940
|
+
* renderer.mount("#container-id");
|
|
3941
|
+
* ```
|
|
3942
|
+
* @deprecated since 0.4.0
|
|
3065
3943
|
* @since 0.0.10
|
|
3066
3944
|
*/
|
|
3067
3945
|
var ASCIIRenderer = /** @class */ (function (_super) {
|
|
3068
3946
|
__extends(ASCIIRenderer, _super);
|
|
3069
|
-
|
|
3947
|
+
/**
|
|
3948
|
+
* Create a new `ASCIIRenderer` by passing in the
|
|
3949
|
+
* {@linkcode GridEnvironment} you want to be rendered.
|
|
3950
|
+
*/
|
|
3951
|
+
function ASCIIRenderer(environment) {
|
|
3070
3952
|
var _this = _super.call(this) || this;
|
|
3071
3953
|
warnOnce$2("As of Flocc v0.5.0, ASCIIEnvironment is **DEPRECATED**. It will be **REMOVED** in v0.6.0. The Terrain helper should be used for 2-dimensional grid-like data, with CanvasRenderer to visualize. Read more about Terrains here: https://flocc.network/docs/terrain");
|
|
3072
3954
|
_this.environment = environment;
|
|
@@ -3075,6 +3957,7 @@
|
|
|
3075
3957
|
return _this;
|
|
3076
3958
|
}
|
|
3077
3959
|
/**
|
|
3960
|
+
* Renders the contents of the `ASCIIRenderer`'s {@linkcode GridEnvironment}.
|
|
3078
3961
|
* @since 0.0.10
|
|
3079
3962
|
*/
|
|
3080
3963
|
ASCIIRenderer.prototype.render = function () {
|
|
@@ -3110,12 +3993,46 @@
|
|
|
3110
3993
|
trace: false
|
|
3111
3994
|
};
|
|
3112
3995
|
/**
|
|
3996
|
+
* A `CanvasRenderer` renders an {@linkcode Environment} spatially in two dimensions.
|
|
3997
|
+
* Importantly, it expects that all {@linkcode Agent}s in the `Environment`
|
|
3998
|
+
* have numeric `"x"` and `"y"` values associated with them.
|
|
3999
|
+
*
|
|
4000
|
+
* `CanvasRenderer`s will render all `Agent`s that are visible in the rendered `Environment` space,
|
|
4001
|
+
* with the color of their `"color"` value (defaulting to black).
|
|
4002
|
+
* Depending on the `"shape"` of the `Agent`, additional data might be needed. `Agent` `"shape"`s can be:
|
|
4003
|
+
* - `"circle"` (default) — Draws a circle centered at the `Agent`'s `"x"` / `"y"` values.
|
|
4004
|
+
* - If the `Agent` has a `"size"` value, uses that for the circle radius (defaults to 1px).
|
|
4005
|
+
* - `"arrow"` — Draws an arrow centered at the `Agent`'s `"x"` / `"y"` values.
|
|
4006
|
+
* - The arrow will point in the direction of the `Agent`s `"vx"` / `"vy"` values. For example, an `Agent` with `"vx" = 1` and `"vy" = 0` will be rendered as an arrow pointing to the right.
|
|
4007
|
+
* - Also uses the `"size" value.
|
|
4008
|
+
* - `"rect"` — Draws a rectangle with the upper-left corner at `"x"` / `"y"`.
|
|
4009
|
+
* - Uses the `Agent`'s `"width"` and `"height"` values for the dimensions of the rectangle.
|
|
4010
|
+
* - `"triangle"` — Draws a triangle centered at the `Agent`'s `"x"` / `"y"` values.
|
|
4011
|
+
* - Also uses the `"size"` value.
|
|
4012
|
+
*
|
|
3113
4013
|
* @since 0.0.11
|
|
3114
4014
|
*/
|
|
3115
4015
|
var CanvasRenderer = /** @class */ (function (_super) {
|
|
3116
4016
|
__extends(CanvasRenderer, _super);
|
|
4017
|
+
/**
|
|
4018
|
+
* The first parameter must be the {@linkcode Environment} that this
|
|
4019
|
+
* `CanvasRenderer` will render.
|
|
4020
|
+
*
|
|
4021
|
+
* The second parameter specifies options, which can include:
|
|
4022
|
+
* - `autoPosition` (*boolean* = `false`) — For `Environment`s using a {@linkcode Network}, whether to automatically position the `Agent`s.
|
|
4023
|
+
* - `background` (*string* = `"transparent"`) — The background color to draw before rendering any `Agent`s.
|
|
4024
|
+
* - `connectionColor` (*string* = `"black"`) — For `Environment`s using a `Network`, the color of lines
|
|
4025
|
+
* - `connectionOpacity` (*number* = `1`) — For `Environment`s using a `Network`, the opacity of lines
|
|
4026
|
+
* - `connectionWidth` (*number* = `1`) — For `Environment`s using a `Network`, the width of lines
|
|
4027
|
+
* - `height` (*number* = `500`) — The height, in pixels, of the canvas on which to render
|
|
4028
|
+
* - `origin` (*{ x: number; y: number }* = `{ x: 0, y: 0 }`) — The coordinate of the upper-left point of the space to be rendered
|
|
4029
|
+
* - `scale` (*number* = `1`) — The scale at which to render (the larger the scale, the smaller the size of the space that is actually rendered)
|
|
4030
|
+
* - `trace` (*boolean* = `false`) — If `true`, the renderer will not clear old drawings, causing the `Agent`s to appear to *trace* their paths across space
|
|
4031
|
+
* - `width` (*number* = `500`) — The width, in pixels, of the canvas on which to render
|
|
4032
|
+
*/
|
|
3117
4033
|
function CanvasRenderer(environment, opts) {
|
|
3118
4034
|
var _this = _super.call(this) || this;
|
|
4035
|
+
/** @hidden */
|
|
3119
4036
|
_this.terrainBuffer = document.createElement("canvas");
|
|
3120
4037
|
_this.environment = environment;
|
|
3121
4038
|
environment.renderers.push(_this);
|
|
@@ -3136,14 +4053,17 @@
|
|
|
3136
4053
|
_this.context.fillRect(0, 0, width, height);
|
|
3137
4054
|
return _this;
|
|
3138
4055
|
}
|
|
4056
|
+
/** @hidden */
|
|
3139
4057
|
CanvasRenderer.prototype.x = function (v) {
|
|
3140
4058
|
var _a = this.opts, origin = _a.origin, scale = _a.scale;
|
|
3141
4059
|
return window.devicePixelRatio * scale * (v - origin.x);
|
|
3142
4060
|
};
|
|
4061
|
+
/** @hidden */
|
|
3143
4062
|
CanvasRenderer.prototype.y = function (v) {
|
|
3144
4063
|
var _a = this.opts, origin = _a.origin, scale = _a.scale;
|
|
3145
4064
|
return window.devicePixelRatio * scale * (v - origin.y);
|
|
3146
4065
|
};
|
|
4066
|
+
/** @hidden */
|
|
3147
4067
|
CanvasRenderer.prototype.createCanvas = function () {
|
|
3148
4068
|
var dpr = window.devicePixelRatio;
|
|
3149
4069
|
var _a = this.opts, width = _a.width, height = _a.height;
|
|
@@ -3152,6 +4072,117 @@
|
|
|
3152
4072
|
canvas.height = height * dpr;
|
|
3153
4073
|
return canvas;
|
|
3154
4074
|
};
|
|
4075
|
+
/** @hidden */
|
|
4076
|
+
CanvasRenderer.prototype.drawPath = function (points, dx, dy) {
|
|
4077
|
+
if (dx === void 0) { dx = 0; }
|
|
4078
|
+
if (dy === void 0) { dy = 0; }
|
|
4079
|
+
var bufferContext = this.buffer.getContext("2d");
|
|
4080
|
+
points.forEach(function (_a, i) {
|
|
4081
|
+
var px = _a[0], py = _a[1];
|
|
4082
|
+
if (i === 0) {
|
|
4083
|
+
bufferContext.moveTo(px + dx, py + dy);
|
|
4084
|
+
}
|
|
4085
|
+
else {
|
|
4086
|
+
bufferContext.lineTo(px + dx, py + dy);
|
|
4087
|
+
}
|
|
4088
|
+
});
|
|
4089
|
+
};
|
|
4090
|
+
/** @hidden */
|
|
4091
|
+
CanvasRenderer.prototype.drawPathWrap = function (points) {
|
|
4092
|
+
var _this = this;
|
|
4093
|
+
var _a = this, width = _a.width, height = _a.height;
|
|
4094
|
+
var right = false;
|
|
4095
|
+
var left = false;
|
|
4096
|
+
var lower = false;
|
|
4097
|
+
var upper = false;
|
|
4098
|
+
points.forEach(function (_a) {
|
|
4099
|
+
var px = _a[0], py = _a[1];
|
|
4100
|
+
if (_this.x(px) >= width)
|
|
4101
|
+
right = true;
|
|
4102
|
+
if (_this.x(px) < 0)
|
|
4103
|
+
left = true;
|
|
4104
|
+
if (_this.y(py) >= height)
|
|
4105
|
+
lower = true;
|
|
4106
|
+
if (_this.y(py) < 0)
|
|
4107
|
+
upper = true;
|
|
4108
|
+
});
|
|
4109
|
+
if (right)
|
|
4110
|
+
this.drawPath(points, -width, 0);
|
|
4111
|
+
if (left)
|
|
4112
|
+
this.drawPath(points, width, 0);
|
|
4113
|
+
if (lower && right)
|
|
4114
|
+
this.drawPath(points, -width, -height);
|
|
4115
|
+
if (upper && right)
|
|
4116
|
+
this.drawPath(points, -width, height);
|
|
4117
|
+
if (lower && left)
|
|
4118
|
+
this.drawPath(points, width, -height);
|
|
4119
|
+
if (upper && left)
|
|
4120
|
+
this.drawPath(points, width, height);
|
|
4121
|
+
if (lower)
|
|
4122
|
+
this.drawPath(points, 0, -height);
|
|
4123
|
+
if (upper)
|
|
4124
|
+
this.drawPath(points, 0, height);
|
|
4125
|
+
};
|
|
4126
|
+
/** @hidden */
|
|
4127
|
+
CanvasRenderer.prototype.drawCircle = function (x, y, r) {
|
|
4128
|
+
var bufferContext = this.buffer.getContext("2d");
|
|
4129
|
+
bufferContext.moveTo(this.x(x), this.y(y));
|
|
4130
|
+
bufferContext.arc(this.x(x), this.y(y), r, 0, 2 * Math.PI);
|
|
4131
|
+
};
|
|
4132
|
+
/** @hidden */
|
|
4133
|
+
CanvasRenderer.prototype.drawCircleWrap = function (x, y, size) {
|
|
4134
|
+
var _a = this, width = _a.width, height = _a.height;
|
|
4135
|
+
if (this.x(x + size) >= width) {
|
|
4136
|
+
this.drawCircle(x - width, y, size);
|
|
4137
|
+
if (this.y(y + size) >= height)
|
|
4138
|
+
this.drawCircle(x - width, y - height, size);
|
|
4139
|
+
if (this.y(y - size) < 0)
|
|
4140
|
+
this.drawCircle(x - width, y + height, size);
|
|
4141
|
+
}
|
|
4142
|
+
if (this.x(x - size) < 0) {
|
|
4143
|
+
this.drawCircle(x + width, y, size);
|
|
4144
|
+
if (this.y(y + size) >= height)
|
|
4145
|
+
this.drawCircle(x + width, y - height, size);
|
|
4146
|
+
if (this.y(y - size) < 0)
|
|
4147
|
+
this.drawCircle(x + width, y + height, size);
|
|
4148
|
+
}
|
|
4149
|
+
if (this.y(y + size) > height)
|
|
4150
|
+
this.drawCircle(x, y - height, size);
|
|
4151
|
+
if (this.y(y - size) < 0)
|
|
4152
|
+
this.drawCircle(x, y + height, size);
|
|
4153
|
+
};
|
|
4154
|
+
/**
|
|
4155
|
+
* Draw a rectangle centered at (x, y). Automatically calculates the offset
|
|
4156
|
+
* for both width and height.
|
|
4157
|
+
* @hidden
|
|
4158
|
+
*/
|
|
4159
|
+
CanvasRenderer.prototype.drawRect = function (x, y, width, height) {
|
|
4160
|
+
var bufferContext = this.buffer.getContext("2d");
|
|
4161
|
+
var dpr = window.devicePixelRatio;
|
|
4162
|
+
bufferContext.fillRect(this.x(x) - (width * dpr) / 2, this.y(y) - (height * dpr) / 2, width * dpr, height * dpr);
|
|
4163
|
+
};
|
|
4164
|
+
/** @hidden */
|
|
4165
|
+
CanvasRenderer.prototype.drawRectWrap = function (x, y, w, h) {
|
|
4166
|
+
var _a = this.opts, width = _a.width, height = _a.height;
|
|
4167
|
+
if (this.x(x + w / 2) >= width) {
|
|
4168
|
+
this.drawRect(x - width, y, w, h);
|
|
4169
|
+
if (this.y(y + h / 2) >= height)
|
|
4170
|
+
this.drawRect(x - width, y - height, w, h);
|
|
4171
|
+
if (this.y(y - height / 2) < 0)
|
|
4172
|
+
this.drawRect(x - width, y + height, w, h);
|
|
4173
|
+
}
|
|
4174
|
+
if (this.x(x - w / 2) < 0) {
|
|
4175
|
+
this.drawRect(x + width, y, w, h);
|
|
4176
|
+
if (this.y(y + h / 2) >= height)
|
|
4177
|
+
this.drawRect(x + width, y - height, w, h);
|
|
4178
|
+
if (this.y(y - height / 2) < 0)
|
|
4179
|
+
this.drawRect(x + width, y + height, w, h);
|
|
4180
|
+
}
|
|
4181
|
+
if (this.y(y + h / 2) > height)
|
|
4182
|
+
this.drawRect(x, y - height, w, h);
|
|
4183
|
+
if (this.y(y - height / 2) < 0)
|
|
4184
|
+
this.drawRect(x, y + height, w, h);
|
|
4185
|
+
};
|
|
3155
4186
|
CanvasRenderer.prototype.render = function () {
|
|
3156
4187
|
var _this = this;
|
|
3157
4188
|
var _a = this, buffer = _a.buffer, context = _a.context, environment = _a.environment, width = _a.width, height = _a.height, opts = _a.opts, terrainBuffer = _a.terrainBuffer;
|
|
@@ -3239,28 +4270,36 @@
|
|
|
3239
4270
|
var _vx = 3 * size * (vx / norm) * dpr;
|
|
3240
4271
|
var _vy = 3 * size * (vy / norm) * dpr;
|
|
3241
4272
|
bufferContext.beginPath();
|
|
3242
|
-
|
|
3243
|
-
|
|
3244
|
-
|
|
3245
|
-
|
|
3246
|
-
|
|
3247
|
-
|
|
4273
|
+
var points = [
|
|
4274
|
+
[_this.x(x) + 1.5 * _vx, _this.y(y) + 1.5 * _vy],
|
|
4275
|
+
[_this.x(x) + _vy / 2, _this.y(y) - _vx / 2],
|
|
4276
|
+
[_this.x(x) - _vy / 2, _this.y(y) + _vx / 2]
|
|
4277
|
+
];
|
|
4278
|
+
_this.drawPath(points);
|
|
4279
|
+
if (environment.opts.torus)
|
|
4280
|
+
_this.drawPathWrap(points);
|
|
3248
4281
|
}
|
|
3249
4282
|
else if (shape === "rect") {
|
|
3250
4283
|
var _h = agent.getData(), _j = _h.width, width_1 = _j === void 0 ? 1 : _j, _k = _h.height, height_1 = _k === void 0 ? 1 : _k;
|
|
3251
|
-
|
|
4284
|
+
_this.drawRect(x, y, width_1, height_1);
|
|
4285
|
+
if (environment.opts.torus)
|
|
4286
|
+
_this.drawRectWrap(x, y, width_1, height_1);
|
|
3252
4287
|
}
|
|
3253
4288
|
else if (shape === "triangle") {
|
|
3254
4289
|
bufferContext.beginPath();
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
4290
|
+
var points = [
|
|
4291
|
+
[_this.x(x), _this.y(y) - size / 2],
|
|
4292
|
+
[_this.x(x) + size / 2, _this.y(y) + size / 2],
|
|
4293
|
+
[_this.x(x) - size / 2, _this.y(y) + size / 2]
|
|
4294
|
+
];
|
|
4295
|
+
_this.drawPath(points);
|
|
4296
|
+
if (environment.opts.torus)
|
|
4297
|
+
_this.drawPathWrap(points);
|
|
3261
4298
|
}
|
|
3262
4299
|
else if (shape === "circle" || shape === undefined) {
|
|
3263
|
-
|
|
4300
|
+
_this.drawCircle(x, y, size * dpr);
|
|
4301
|
+
if (environment.opts.torus)
|
|
4302
|
+
_this.drawCircleWrap(x, y, size);
|
|
3264
4303
|
}
|
|
3265
4304
|
bufferContext.fill();
|
|
3266
4305
|
if (text) {
|
|
@@ -3282,22 +4321,6 @@
|
|
|
3282
4321
|
return CanvasRenderer;
|
|
3283
4322
|
}(AbstractRenderer));
|
|
3284
4323
|
|
|
3285
|
-
/**
|
|
3286
|
-
* Maps a number x, from the given domain aMin --> aMax,
|
|
3287
|
-
* onto the given range bMin --> bMax.
|
|
3288
|
-
* Ex: remap(5, 0, 10, 0, 100) => 50.
|
|
3289
|
-
* @param {number} x
|
|
3290
|
-
* @param {number} aMin
|
|
3291
|
-
* @param {number} aMax
|
|
3292
|
-
* @param {number} bMin
|
|
3293
|
-
* @param {number} bMax
|
|
3294
|
-
* @returns {number} The remapped value.
|
|
3295
|
-
* @since 0.0.5
|
|
3296
|
-
*/
|
|
3297
|
-
function remap(x, aMin, aMax, bMin, bMax) {
|
|
3298
|
-
return bMin + ((bMax - bMin) * (x - aMin)) / (aMax - aMin);
|
|
3299
|
-
}
|
|
3300
|
-
|
|
3301
4324
|
/// <reference path="../types/NRange.d.ts" />
|
|
3302
4325
|
function extractRoundNumbers(range) {
|
|
3303
4326
|
var min = range.min, max = range.max;
|
|
@@ -3609,7 +4632,7 @@
|
|
|
3609
4632
|
if (opts.autoScroll && t >= width) {
|
|
3610
4633
|
x -= t - width;
|
|
3611
4634
|
}
|
|
3612
|
-
else if (opts.autoScale
|
|
4635
|
+
else if (opts.autoScale) {
|
|
3613
4636
|
x *= width / t;
|
|
3614
4637
|
}
|
|
3615
4638
|
return x | 0;
|
|
@@ -3657,7 +4680,7 @@
|
|
|
3657
4680
|
context.restore();
|
|
3658
4681
|
// draw time values for horizontal axis
|
|
3659
4682
|
var min = opts.autoScroll && t >= width ? t - width : 0;
|
|
3660
|
-
var max = opts.autoScale
|
|
4683
|
+
var max = opts.autoScale ? Math.max(t, 5) : width;
|
|
3661
4684
|
var timeRange = { min: min, max: max };
|
|
3662
4685
|
var timeMarkers = extractRoundNumbers(timeRange);
|
|
3663
4686
|
context.save();
|
|
@@ -3737,14 +4760,61 @@
|
|
|
3737
4760
|
};
|
|
3738
4761
|
var escapeStringQuotes = function (s) { return "\"" + s.replace(/"/g, '\\"') + "\""; };
|
|
3739
4762
|
/**
|
|
4763
|
+
* A `TableRenderer` renders an HTML table (for browsers only) or CSV (comma-separated value)
|
|
4764
|
+
* representation of {@linkcode Agent} data.
|
|
4765
|
+
*
|
|
4766
|
+
* ```js
|
|
4767
|
+
* for (let i = 0; i < 3; i++) {
|
|
4768
|
+
* environment.addAgent(new Agent({
|
|
4769
|
+
* x: i * 10,
|
|
4770
|
+
* y: i - 2
|
|
4771
|
+
* }));
|
|
4772
|
+
* }
|
|
4773
|
+
*
|
|
4774
|
+
* const renderer = new TableRenderer(environment);
|
|
4775
|
+
* renderer.columns = ['x', 'y'];
|
|
4776
|
+
* renderer.mount('#container');
|
|
4777
|
+
* environment.tick();
|
|
4778
|
+
* ```
|
|
4779
|
+
*
|
|
4780
|
+
* The `TableRenderer` renders:
|
|
4781
|
+
*
|
|
4782
|
+
* |x |y |
|
|
4783
|
+
* |----|----|
|
|
4784
|
+
* |0 |-2 |
|
|
4785
|
+
* |10 |-1 |
|
|
4786
|
+
* |20 |0 |
|
|
4787
|
+
*
|
|
3740
4788
|
* @since 0.5.0
|
|
3741
4789
|
*/
|
|
3742
4790
|
var TableRenderer = /** @class */ (function (_super) {
|
|
3743
4791
|
__extends(TableRenderer, _super);
|
|
4792
|
+
/**
|
|
4793
|
+
* The first parameter must be the {@linkcode Environment} that this
|
|
4794
|
+
* `TableRenderer` will render.
|
|
4795
|
+
*
|
|
4796
|
+
* The second parameter specifies options, which can include:
|
|
4797
|
+
* - `"type"` (`"csv"` | `"table"` = `"table"`) — Whether to render output in CSV or HTML `<table>` format
|
|
4798
|
+
* - `"filter"` — Include a function (`Agent` => `boolean`) to specify which rows to include in the output. For example, if you only want to include `Agent`s with an x value greater than 100:
|
|
4799
|
+
* ```js
|
|
4800
|
+
* const renderer = new TableRenderer(environment, {
|
|
4801
|
+
* filter: agent => {
|
|
4802
|
+
* return agent.get('x') > 100;
|
|
4803
|
+
* }
|
|
4804
|
+
* });
|
|
4805
|
+
* ```
|
|
4806
|
+
* - `"limit"` (*number* = `Infinity`) — The maximum number of rows (`Agent`s) to render. If using a `filter` function, applies the `limit` *after* filtering.
|
|
4807
|
+
* - `"sortKey"` (*string* = `null`) — Sort the `Agent` data by this key of data
|
|
4808
|
+
* - `"order"` (`"asc"` | `"desc"` = `"desc"`) — When using a `"sortKey"`, specify whether `Agent`s should be listed in *asc*ending or *desc*ending order
|
|
4809
|
+
* - `"precision"` (*number* = `3`) — For floating point values, the number of decimal places to display
|
|
4810
|
+
* - `"refresh"` (*number* = `500`) — The number of milliseconds that should elapse between re-rendering (if this happens too quickly the effect can be visually jarring)
|
|
4811
|
+
*/
|
|
3744
4812
|
function TableRenderer(environment, options) {
|
|
3745
4813
|
if (options === void 0) { options = {}; }
|
|
3746
4814
|
var _this = _super.call(this) || this;
|
|
4815
|
+
/** @hidden */
|
|
3747
4816
|
_this.lastRendered = +new Date();
|
|
4817
|
+
/** @hidden */
|
|
3748
4818
|
_this.opts = Object.assign({}, defaultTableRendererOptions);
|
|
3749
4819
|
_this.environment = environment;
|
|
3750
4820
|
environment.renderers.push(_this);
|
|
@@ -3754,7 +4824,16 @@
|
|
|
3754
4824
|
}
|
|
3755
4825
|
/**
|
|
3756
4826
|
* Mount this renderer to a DOM element. Pass either a string representing a
|
|
3757
|
-
* CSS selector matching the element
|
|
4827
|
+
* CSS selector matching the element or the element itself.
|
|
4828
|
+
*
|
|
4829
|
+
* ```js
|
|
4830
|
+
* // mounts the renderer to the element with the ID `container`
|
|
4831
|
+
* renderer.mount('#container');
|
|
4832
|
+
*
|
|
4833
|
+
* // mounts the renderer to the element itself
|
|
4834
|
+
* const container = document.getElementById('container');
|
|
4835
|
+
* renderer.mount(container);
|
|
4836
|
+
* ```
|
|
3758
4837
|
* @override
|
|
3759
4838
|
* @param {string | HTMLElement} el
|
|
3760
4839
|
*/
|
|
@@ -3767,6 +4846,7 @@
|
|
|
3767
4846
|
}
|
|
3768
4847
|
this.table = container;
|
|
3769
4848
|
};
|
|
4849
|
+
/** @hidden */
|
|
3770
4850
|
TableRenderer.prototype.serializeColumns = function (joiner, start, end, escape) {
|
|
3771
4851
|
if (start === void 0) { start = ""; }
|
|
3772
4852
|
if (end === void 0) { end = ""; }
|
|
@@ -3778,6 +4858,7 @@
|
|
|
3778
4858
|
return "";
|
|
3779
4859
|
return start + columns.join(joiner) + end;
|
|
3780
4860
|
};
|
|
4861
|
+
/** @hidden */
|
|
3781
4862
|
TableRenderer.prototype.serializeRows = function (cellJoiner, rowJoiner, start, end, rowStart, rowEnd, escape) {
|
|
3782
4863
|
var _this = this;
|
|
3783
4864
|
if (start === void 0) { start = ""; }
|
|
@@ -3826,6 +4907,7 @@
|
|
|
3826
4907
|
.join(rowJoiner) +
|
|
3827
4908
|
end);
|
|
3828
4909
|
};
|
|
4910
|
+
/** @hidden */
|
|
3829
4911
|
TableRenderer.prototype.renderCSV = function () {
|
|
3830
4912
|
var columns = this.serializeColumns(",", "", "", true);
|
|
3831
4913
|
if (columns === "")
|
|
@@ -3835,12 +4917,28 @@
|
|
|
3835
4917
|
return columns;
|
|
3836
4918
|
return columns + "\n" + rows;
|
|
3837
4919
|
};
|
|
4920
|
+
/** @hidden */
|
|
3838
4921
|
TableRenderer.prototype.renderHTMLTable = function () {
|
|
3839
4922
|
var thead = this.serializeColumns("</td><td>", "<thead><tr><td>", "</td></tr></thead>");
|
|
3840
4923
|
var tbody = this.serializeRows("</td><td>", "", "<tbody>", "</tbody>", "<tr><td>", "</td></tr>");
|
|
3841
4924
|
return "<table>" + thead + tbody + "</table>";
|
|
3842
4925
|
};
|
|
3843
4926
|
/**
|
|
4927
|
+
* Returns the outer HTML of the table or the CSV data as a string. This can be useful for exporting data, particularly in a Node.js environment as opposed to in a browser. For instance, in a Node.js script, you could write the CSV data to a file as follows:
|
|
4928
|
+
*
|
|
4929
|
+
* ```js
|
|
4930
|
+
* const fs = require('fs'); // import the file system module
|
|
4931
|
+
*
|
|
4932
|
+
* const environment = new Environment();
|
|
4933
|
+
* for (let i = 0; i < 3; i++) environment.addAgent(new Agent({ i }));
|
|
4934
|
+
*
|
|
4935
|
+
* const renderer = new TableRenderer(environment, { type: 'csv' });
|
|
4936
|
+
* renderer.columns = ['i'];
|
|
4937
|
+
*
|
|
4938
|
+
* // write the TableRenderer's output to a CSV file named data.csv
|
|
4939
|
+
* fs.writeFileSync('./data.csv', renderer.output());
|
|
4940
|
+
* ```
|
|
4941
|
+
*
|
|
3844
4942
|
* @since 0.5.0
|
|
3845
4943
|
*/
|
|
3846
4944
|
TableRenderer.prototype.output = function () {
|
|
@@ -3866,22 +4964,6 @@
|
|
|
3866
4964
|
return TableRenderer;
|
|
3867
4965
|
}(AbstractRenderer));
|
|
3868
4966
|
|
|
3869
|
-
/**
|
|
3870
|
-
* Restricts a number x to the range min --> max.
|
|
3871
|
-
* @param {number} x
|
|
3872
|
-
* @param {number} min
|
|
3873
|
-
* @param {number} max
|
|
3874
|
-
* @return {number} The clamped value.
|
|
3875
|
-
* @since 0.0.5
|
|
3876
|
-
*/
|
|
3877
|
-
function clamp(x, min, max) {
|
|
3878
|
-
if (x < min)
|
|
3879
|
-
return min;
|
|
3880
|
-
if (x > max)
|
|
3881
|
-
return max;
|
|
3882
|
-
return x;
|
|
3883
|
-
}
|
|
3884
|
-
|
|
3885
4967
|
var PADDING_AT_BOTTOM$1 = 60;
|
|
3886
4968
|
var PADDING_AT_LEFT$1 = 60;
|
|
3887
4969
|
var isAxisObject = function (obj) {
|
|
@@ -3898,12 +4980,48 @@
|
|
|
3898
4980
|
};
|
|
3899
4981
|
var warnOnce$3 = once(console.warn.bind(console));
|
|
3900
4982
|
/**
|
|
4983
|
+
* A `Heatmap` can be used to visualize the distribution of {@linkcode Agent}s across two metrics.
|
|
4984
|
+
* While {@linkcode Histogram}s are useful for showing the distribution of `Agent`s along a single metric
|
|
4985
|
+
* (or on multiple metrics using the same scale), a `Heatmap` can show how two metrics relate to one another —
|
|
4986
|
+
* correlation, inverse correlation, in a nonlinear manner, randomly (no correlation), etc.
|
|
4987
|
+
*
|
|
4988
|
+
* <img src="https://cms.flocc.network/wp-content/uploads/2020/11/heatmap-basic.png" />
|
|
4989
|
+
*
|
|
4990
|
+
* Note above that, although the output appears similar to what a {@linkcode CanvasRenderer} might output, the `y` axis is reversed here — low values are at the bottom and high at the top, whereas on a `CanvasRenderer` high values are at the bottom and low at the top.
|
|
4991
|
+
*
|
|
3901
4992
|
* @since 0.5.8
|
|
3902
4993
|
*/
|
|
3903
4994
|
var Heatmap = /** @class */ (function (_super) {
|
|
3904
4995
|
__extends(Heatmap, _super);
|
|
4996
|
+
/**
|
|
4997
|
+
* The first parameter must be the {@linkcode Environment} that this
|
|
4998
|
+
* `Heatmap` will render.
|
|
4999
|
+
*
|
|
5000
|
+
* The second parameter specifies options, which can include:
|
|
5001
|
+
* - `from` (*string* = `"white"`) — The color (name, hex value, or RGB) to draw when a cell contains `0` {@linkcode Agent}s
|
|
5002
|
+
* - `to` (*string* = `"black"`) — The color (name, hex value, or RGB) to draw when a cell contains the highest number of `Agent`s
|
|
5003
|
+
* - `x` and `y` can be either:
|
|
5004
|
+
* - *string* = `"x"`/`"y"` respectively — The name of `Agent` data to measure along the `x`/`y` axis
|
|
5005
|
+
* - *{ buckets: number; key: string; min: number; max: number }* = `{ buckets: 10, key: 'x' | 'y', min: 0, max: 1 }` — Include the number of buckets to divide the range `min → max` into, along with the name of `Agent` data
|
|
5006
|
+
* - `width` (*number* = `500`) — The width, in pixels, of the canvas on which to render
|
|
5007
|
+
* - `height` (*number* = `500`) — The height, in pixels, of the canvas on which to render
|
|
5008
|
+
* - `scale` (either `"relative"` or `"fixed"`, defaults to `"relative"`)
|
|
5009
|
+
* - `"relative"` — The maximum number of `Agent`s in any single cell is automatically used as the highest value in the scale. This updates over time based on `Agent` distribution.
|
|
5010
|
+
* - `"fixed"` — You supply the number to use as the maximum value (see `max` below).
|
|
5011
|
+
* - `max` (optional, *number*) — If you use `scale = "fixed"`, then setting a `max` will cause cells with that number (or higher) of `Agent`s to be drawn using the `to` color.
|
|
5012
|
+
*
|
|
5013
|
+
* ```js
|
|
5014
|
+
* // plots the correlation between age of agents (on the x-axis)
|
|
5015
|
+
* // vs. their wealth (on the y-axis)
|
|
5016
|
+
* const heatmap = new Heatmap(environment, {
|
|
5017
|
+
* x: 'age',
|
|
5018
|
+
* y: 'wealth'
|
|
5019
|
+
* });
|
|
5020
|
+
* ```
|
|
5021
|
+
*/
|
|
3905
5022
|
function Heatmap(environment, opts) {
|
|
3906
5023
|
var _this = _super.call(this) || this;
|
|
5024
|
+
/** @hidden */
|
|
3907
5025
|
_this.opts = defaultHeatmapOptions;
|
|
3908
5026
|
_this.environment = environment;
|
|
3909
5027
|
_this.opts = Object.assign({}, _this.opts, opts);
|
|
@@ -3922,7 +5040,7 @@
|
|
|
3922
5040
|
}
|
|
3923
5041
|
/**
|
|
3924
5042
|
* Map a value (on the range x-min to x-max) onto canvas space to draw it along the x-axis.
|
|
3925
|
-
* @
|
|
5043
|
+
* @hidden
|
|
3926
5044
|
*/
|
|
3927
5045
|
Heatmap.prototype.x = function (value) {
|
|
3928
5046
|
var width = this.width;
|
|
@@ -3930,12 +5048,13 @@
|
|
|
3930
5048
|
};
|
|
3931
5049
|
/**
|
|
3932
5050
|
* Map a value (on the range y-min to y-max) onto canvas space to draw it along the y-axis.
|
|
3933
|
-
* @
|
|
5051
|
+
* @hidden
|
|
3934
5052
|
*/
|
|
3935
5053
|
Heatmap.prototype.y = function (value) {
|
|
3936
5054
|
var height = this.height;
|
|
3937
5055
|
return remap(value, this.getMin("y"), this.getMax("y"), height - PADDING_AT_BOTTOM$1, 0);
|
|
3938
5056
|
};
|
|
5057
|
+
/** @hidden */
|
|
3939
5058
|
Heatmap.prototype.getKey = function (axis) {
|
|
3940
5059
|
var a = this.opts[axis];
|
|
3941
5060
|
if (isAxisObject(a)) {
|
|
@@ -3945,12 +5064,14 @@
|
|
|
3945
5064
|
return a;
|
|
3946
5065
|
}
|
|
3947
5066
|
};
|
|
5067
|
+
/** @hidden */
|
|
3948
5068
|
Heatmap.prototype.getBuckets = function (axis) {
|
|
3949
5069
|
var a = this.opts[axis];
|
|
3950
5070
|
if (isAxisObject(a) && a.hasOwnProperty("buckets"))
|
|
3951
5071
|
return a.buckets;
|
|
3952
5072
|
return 10;
|
|
3953
5073
|
};
|
|
5074
|
+
/** @hidden */
|
|
3954
5075
|
Heatmap.prototype.getMin = function (axis) {
|
|
3955
5076
|
var a = this.opts[axis];
|
|
3956
5077
|
if (isAxisObject(a) && a.hasOwnProperty("min")) {
|
|
@@ -3960,6 +5081,7 @@
|
|
|
3960
5081
|
return 0;
|
|
3961
5082
|
}
|
|
3962
5083
|
};
|
|
5084
|
+
/** @hidden */
|
|
3963
5085
|
Heatmap.prototype.getMax = function (axis) {
|
|
3964
5086
|
var a = this.opts[axis];
|
|
3965
5087
|
if (isAxisObject(a) && a.hasOwnProperty("max")) {
|
|
@@ -3969,6 +5091,7 @@
|
|
|
3969
5091
|
return 1;
|
|
3970
5092
|
}
|
|
3971
5093
|
};
|
|
5094
|
+
/** @hidden */
|
|
3972
5095
|
Heatmap.prototype.drawMarkers = function () {
|
|
3973
5096
|
var _a = this, context = _a.context, width = _a.width, height = _a.height;
|
|
3974
5097
|
var _b = this.opts, from = _b.from, to = _b.to;
|
|
@@ -4021,6 +5144,7 @@
|
|
|
4021
5144
|
}
|
|
4022
5145
|
}
|
|
4023
5146
|
};
|
|
5147
|
+
/** @hidden */
|
|
4024
5148
|
Heatmap.prototype.updateScale = function () {
|
|
4025
5149
|
var _a = this, context = _a.context, environment = _a.environment, height = _a.height;
|
|
4026
5150
|
var scale = this.opts.scale;
|
|
@@ -4042,6 +5166,7 @@
|
|
|
4042
5166
|
this.lastUpdatedScale = new Date();
|
|
4043
5167
|
}
|
|
4044
5168
|
};
|
|
5169
|
+
/** @hidden */
|
|
4045
5170
|
Heatmap.prototype.drawRectangles = function () {
|
|
4046
5171
|
var _a = this, canvas = _a.canvas, environment = _a.environment, width = _a.width, height = _a.height;
|
|
4047
5172
|
var _b = this.opts, scale = _b.scale, from = _b.from, to = _b.to;
|
|
@@ -4068,11 +5193,13 @@
|
|
|
4068
5193
|
}
|
|
4069
5194
|
context.globalAlpha = 1;
|
|
4070
5195
|
};
|
|
5196
|
+
/** @hidden */
|
|
4071
5197
|
Heatmap.prototype.resetBuckets = function () {
|
|
4072
5198
|
for (var i = 0; i < this.getBuckets("x") * this.getBuckets("y"); i++) {
|
|
4073
5199
|
this.buckets[i] = 0;
|
|
4074
5200
|
}
|
|
4075
5201
|
};
|
|
5202
|
+
/** @hidden */
|
|
4076
5203
|
Heatmap.prototype.updateBuckets = function () {
|
|
4077
5204
|
var _this = this;
|
|
4078
5205
|
var environment = this.environment;
|
|
@@ -4115,135 +5242,9 @@
|
|
|
4115
5242
|
}(AbstractRenderer));
|
|
4116
5243
|
|
|
4117
5244
|
/**
|
|
4118
|
-
*
|
|
4119
|
-
* returns a value from a normal/Gaussian distribution.
|
|
4120
|
-
* @param {number} mean
|
|
4121
|
-
* @param {number} sd
|
|
4122
|
-
* @returns {number}
|
|
4123
|
-
* @since 0.0.8
|
|
4124
|
-
*/
|
|
4125
|
-
function gaussian(mean, sd) {
|
|
4126
|
-
if (mean === void 0) { mean = 0; }
|
|
4127
|
-
if (sd === void 0) { sd = 1; }
|
|
4128
|
-
var y, x1, x2, w;
|
|
4129
|
-
do {
|
|
4130
|
-
x1 = 2 * uniform() - 1;
|
|
4131
|
-
x2 = 2 * uniform() - 1;
|
|
4132
|
-
w = x1 * x1 + x2 * x2;
|
|
4133
|
-
} while (w >= 1);
|
|
4134
|
-
w = Math.sqrt((-2 * Math.log(w)) / w);
|
|
4135
|
-
y = x1 * w;
|
|
4136
|
-
return y * sd + mean;
|
|
4137
|
-
}
|
|
4138
|
-
|
|
4139
|
-
/// <reference path="../types/Point.d.ts" />
|
|
4140
|
-
/**
|
|
4141
|
-
* Finds the Manhattan distance between `p1` and `p2`.
|
|
4142
|
-
* The inputs may be plain objects
|
|
4143
|
-
* with `x`, `y`, and/or `z` keys, or Agent-like objects who have
|
|
4144
|
-
* `x`, `y`, and/or `z` data.
|
|
4145
|
-
* @param {Point|Agent} p1
|
|
4146
|
-
* @param {Point|Agent} p2
|
|
4147
|
-
* @return {number} The Manhattan distance between p1 and p2.
|
|
4148
|
-
* @since 0.0.12
|
|
4149
|
-
*/
|
|
4150
|
-
function manhattanDistance(p1, p2) {
|
|
4151
|
-
var x1 = (p1 instanceof Agent ? p1.get("x") : p1.x) || 0;
|
|
4152
|
-
var y1 = (p1 instanceof Agent ? p1.get("y") : p1.y) || 0;
|
|
4153
|
-
var z1 = (p1 instanceof Agent ? p1.get("z") : p1.z) || 0;
|
|
4154
|
-
var x2 = (p2 instanceof Agent ? p2.get("x") : p2.x) || 0;
|
|
4155
|
-
var y2 = (p2 instanceof Agent ? p2.get("y") : p2.y) || 0;
|
|
4156
|
-
var z2 = (p2 instanceof Agent ? p2.get("z") : p2.z) || 0;
|
|
4157
|
-
var dx = Math.abs(x2 - x1);
|
|
4158
|
-
var dy = Math.abs(y2 - y1);
|
|
4159
|
-
var dz = Math.abs(z2 - z1);
|
|
4160
|
-
// distance for toroidal environments
|
|
4161
|
-
if (p1 instanceof Agent &&
|
|
4162
|
-
p2 instanceof Agent &&
|
|
4163
|
-
p1.environment &&
|
|
4164
|
-
p2.environment &&
|
|
4165
|
-
p1.environment === p2.environment &&
|
|
4166
|
-
p1.environment.width &&
|
|
4167
|
-
p1.environment.height &&
|
|
4168
|
-
p1.environment.opts.torus) {
|
|
4169
|
-
var environment = p1.environment;
|
|
4170
|
-
var width = environment.width, height = environment.height;
|
|
4171
|
-
if (dx > width / 2)
|
|
4172
|
-
dx = width - dx;
|
|
4173
|
-
if (dy > height / 2)
|
|
4174
|
-
dy = height - dy;
|
|
4175
|
-
}
|
|
4176
|
-
return dx + dy + dz;
|
|
4177
|
-
}
|
|
4178
|
-
|
|
4179
|
-
/**
|
|
4180
|
-
* Seed a pseudo-random number generator with a value.
|
|
4181
|
-
* This can be used to produce predictable pseudo-random numbers.
|
|
4182
|
-
* When calling `utils.random`, `utils.sample`, or other functions
|
|
4183
|
-
* relying on randomness with the same initial seed, the values
|
|
4184
|
-
* generated will always be the same.
|
|
4185
|
-
*
|
|
4186
|
-
* Predictable randomness can be turned off by calling `seed(null)`, or reset
|
|
4187
|
-
* by calling `seed(value)` again with the initial value you used.
|
|
4188
|
-
* @param value
|
|
4189
|
-
* @since 0.5.0
|
|
4190
|
-
*/
|
|
4191
|
-
var seed = function (value) { return PRNG.seed(value); };
|
|
4192
|
-
|
|
4193
|
-
/**
|
|
4194
|
-
* Find the standard deviation of an Array of numbers.
|
|
4195
|
-
* @param {Array<number>} arr
|
|
4196
|
-
* @returns {number}
|
|
4197
|
-
* @since 0.0.16
|
|
4198
|
-
*/
|
|
4199
|
-
function stdDev(arr) {
|
|
4200
|
-
if (arr.length === 0)
|
|
4201
|
-
return null;
|
|
4202
|
-
var ave = mean(arr);
|
|
4203
|
-
return Math.sqrt(mean(arr.map(function (x) { return (x - ave) * (x - ave); })));
|
|
4204
|
-
}
|
|
4205
|
-
|
|
4206
|
-
/**
|
|
4207
|
-
* @since 0.1.4
|
|
5245
|
+
* The current version of the Flocc library.
|
|
4208
5246
|
*/
|
|
4209
|
-
|
|
4210
|
-
if (width === void 0) { width = 0; }
|
|
4211
|
-
var output = str;
|
|
4212
|
-
while (output.length < width)
|
|
4213
|
-
output = "0" + output;
|
|
4214
|
-
return output;
|
|
4215
|
-
}
|
|
4216
|
-
|
|
4217
|
-
|
|
4218
|
-
|
|
4219
|
-
var utils = /*#__PURE__*/Object.freeze({
|
|
4220
|
-
__proto__: null,
|
|
4221
|
-
clamp: clamp,
|
|
4222
|
-
distance: distance,
|
|
4223
|
-
gaussian: gaussian,
|
|
4224
|
-
gcd: gcd,
|
|
4225
|
-
manhattanDistance: manhattanDistance,
|
|
4226
|
-
lerp: lerp,
|
|
4227
|
-
remap: remap,
|
|
4228
|
-
random: random,
|
|
4229
|
-
sample: sample$1,
|
|
4230
|
-
sampler: sampler,
|
|
4231
|
-
seed: seed,
|
|
4232
|
-
series: series,
|
|
4233
|
-
shuffle: shuffle,
|
|
4234
|
-
sum: sum,
|
|
4235
|
-
max: max,
|
|
4236
|
-
mean: mean,
|
|
4237
|
-
median: median,
|
|
4238
|
-
min: min,
|
|
4239
|
-
percentile: percentile,
|
|
4240
|
-
stdDev: stdDev,
|
|
4241
|
-
uniform: uniform,
|
|
4242
|
-
uuid: uuid$1,
|
|
4243
|
-
zfill: zfill
|
|
4244
|
-
});
|
|
4245
|
-
|
|
4246
|
-
var version = "0.5.18";
|
|
5247
|
+
var version = "0.5.21";
|
|
4247
5248
|
|
|
4248
5249
|
exports.ASCIIRenderer = ASCIIRenderer;
|
|
4249
5250
|
exports.Agent = Agent;
|