@thi.ng/boids 0.1.15 → 1.0.0
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/CHANGELOG.md +32 -1
- package/README.md +59 -4
- package/accel.d.ts +12 -0
- package/accel.js +16 -0
- package/api.d.ts +32 -15
- package/api.js +0 -1
- package/behaviors/alignment.d.ts +3 -0
- package/behaviors/alignment.js +23 -0
- package/behaviors/attraction.d.ts +4 -0
- package/behaviors/attraction.js +25 -0
- package/behaviors/braitenberg.d.ts +18 -0
- package/behaviors/braitenberg.js +22 -0
- package/behaviors/cohesion.d.ts +3 -0
- package/behaviors/cohesion.js +23 -0
- package/behaviors/dynamic.d.ts +19 -0
- package/behaviors/dynamic.js +19 -0
- package/behaviors/follow.d.ts +14 -0
- package/behaviors/follow.js +18 -0
- package/behaviors/mixed.d.ts +4 -0
- package/behaviors/mixed.js +13 -0
- package/behaviors/path.d.ts +4 -0
- package/behaviors/path.js +18 -0
- package/behaviors/probabilistic.d.ts +4 -0
- package/behaviors/probabilistic.js +24 -0
- package/behaviors/separation.d.ts +3 -0
- package/behaviors/separation.js +28 -0
- package/behaviors/update.d.ts +4 -0
- package/behaviors/update.js +13 -0
- package/boid.d.ts +11 -18
- package/boid.js +20 -88
- package/constrain.d.ts +7 -0
- package/constrain.js +44 -0
- package/flock.d.ts +27 -0
- package/flock.js +27 -0
- package/index.d.ts +11 -0
- package/index.js +11 -0
- package/internal/ensure.d.ts +3 -0
- package/internal/ensure.js +5 -0
- package/package.json +61 -9
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
-
- **Last updated**:
|
|
3
|
+
- **Last updated**: 2024-01-23T15:58:26Z
|
|
4
4
|
- **Generator**: [thi.ng/monopub](https://thi.ng/monopub)
|
|
5
5
|
|
|
6
6
|
All notable changes to this project will be documented in this file.
|
|
@@ -9,6 +9,37 @@ See [Conventional Commits](https://conventionalcommits.org/) for commit guidelin
|
|
|
9
9
|
**Note:** Unlisted _patch_ versions only involve non-code or otherwise excluded changes
|
|
10
10
|
and/or version bumps of transitive dependencies.
|
|
11
11
|
|
|
12
|
+
# [1.0.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/boids@1.0.0) (2024-01-23)
|
|
13
|
+
|
|
14
|
+
#### 🛑 Breaking changes
|
|
15
|
+
|
|
16
|
+
- major refactoring/restructuring, new behavior system ([22cb42d](https://github.com/thi-ng/umbrella/commit/22cb42d))
|
|
17
|
+
- BREAKING CHANGE: major refactoring/restructuring, new behavior system
|
|
18
|
+
- modular behavior system with currently these 7 behaviors:
|
|
19
|
+
- alignment()
|
|
20
|
+
- attractPolyline()
|
|
21
|
+
- braitenberg2()
|
|
22
|
+
- cohesion()
|
|
23
|
+
- dynamicTarget()
|
|
24
|
+
- followPolyline()
|
|
25
|
+
- separation()
|
|
26
|
+
- add blendedBehaviorUpdate()
|
|
27
|
+
- add clamp2/3() and wrap2/3() global constraints
|
|
28
|
+
- add Flock class (+ defFlock() factory fn)
|
|
29
|
+
- update/simplify Boid class and 2d/3d factory fns
|
|
30
|
+
- update BoidOpts
|
|
31
|
+
- add doc strings
|
|
32
|
+
- add/update deps
|
|
33
|
+
|
|
34
|
+
#### 🚀 Features
|
|
35
|
+
|
|
36
|
+
- update dynamicTarget() ([647d7e1](https://github.com/thi-ng/umbrella/commit/647d7e1))
|
|
37
|
+
- add radius param
|
|
38
|
+
|
|
39
|
+
#### 🩹 Bug fixes
|
|
40
|
+
|
|
41
|
+
- update separation() behavior neighbor lookup ([c8ece89](https://github.com/thi-ng/umbrella/commit/c8ece89))
|
|
42
|
+
|
|
12
43
|
### [0.1.3](https://github.com/thi-ng/umbrella/tree/@thi.ng/boids@0.1.3) (2023-11-09)
|
|
13
44
|
|
|
14
45
|
#### ♻️ Refactoring
|
package/README.md
CHANGED
|
@@ -12,6 +12,8 @@ This is a standalone project, maintained as part of the
|
|
|
12
12
|
anti-framework.
|
|
13
13
|
|
|
14
14
|
- [About](#about)
|
|
15
|
+
- [Available behaviors](#available-behaviors)
|
|
16
|
+
- [Acceleration structures](#acceleration-structures)
|
|
15
17
|
- [Status](#status)
|
|
16
18
|
- [Installation](#installation)
|
|
17
19
|
- [Dependencies](#dependencies)
|
|
@@ -22,7 +24,57 @@ anti-framework.
|
|
|
22
24
|
|
|
23
25
|
## About
|
|
24
26
|
|
|
25
|
-
n-dimensional boids simulation with
|
|
27
|
+
n-dimensional boids simulation with modular behavior system.
|
|
28
|
+
|
|
29
|
+
The API of this package is still unstable, but the underlying implementations
|
|
30
|
+
have been used in many of the author's projects since ~2005... The agent/boid
|
|
31
|
+
behaviors are fully modular and can be highly customized via the given
|
|
32
|
+
parameters (which can also be dynamically/spatially adjusted). As with other
|
|
33
|
+
thi.ng packages, the visual representation of the boids is entirely separate and
|
|
34
|
+
out of scope of this package. This package only deals with the simulation of
|
|
35
|
+
agents, their behavioral aspects and essentially only processes points in space
|
|
36
|
+
(and their directions, forces)...
|
|
37
|
+
|
|
38
|
+
### Available behaviors
|
|
39
|
+
|
|
40
|
+
The following behavior building blocks are provided. All of them can be freely
|
|
41
|
+
combined (incl. multiple instances with different configurations) and assigned
|
|
42
|
+
to individual boids (or groups of them). Each behavior also has an associated
|
|
43
|
+
weight to adjust its impact on the overall movement of the boids (also
|
|
44
|
+
dynamically adjustable).
|
|
45
|
+
|
|
46
|
+
- [`alignment()`](https://docs.thi.ng/umbrella/boids/functions/alignment.html):
|
|
47
|
+
Steer towards the average direction of neighbors within given radius
|
|
48
|
+
- [`attractPolyline()`](https://docs.thi.ng/umbrella/boids/functions/attractPolyline.html):
|
|
49
|
+
Steer towards the nearest point on a pre-configured polyline (or polygon)
|
|
50
|
+
- [`braitenberg2()`](https://docs.thi.ng/umbrella/boids/functions/braitenberg2.html):
|
|
51
|
+
Field-based 3-sensor (left/right/center) Braitenberg vehicle steering
|
|
52
|
+
- [`cohesion()`](https://docs.thi.ng/umbrella/boids/functions/cohesion.html):
|
|
53
|
+
Steer towards the centroid of neighbors within given radius
|
|
54
|
+
- [`dynamicTarget()`](https://docs.thi.ng/umbrella/boids/functions/dynamicTarget.html):
|
|
55
|
+
Steer towards user defined (dynamically changeable) location(s)
|
|
56
|
+
- [`followPolyline()`](https://docs.thi.ng/umbrella/boids/functions/followPolyline.html):
|
|
57
|
+
Steer towards the following/next point on a pre-configured polyline (or
|
|
58
|
+
polygon)
|
|
59
|
+
- [`separation()`](https://docs.thi.ng/umbrella/boids/functions/separation.html):
|
|
60
|
+
Steer away from neighbors within given radius
|
|
61
|
+
|
|
62
|
+
### Acceleration structures
|
|
63
|
+
|
|
64
|
+
Intended for behaviors requiring neighbor lookups, the package defines &
|
|
65
|
+
utilizes the [`IBoidAccel`
|
|
66
|
+
interface](https://docs.thi.ng/umbrella/boids/interfaces/IBoidAccel.html). It's
|
|
67
|
+
recommended to use a compatible spatial acceleration structure such as
|
|
68
|
+
[`HashGrid2` or
|
|
69
|
+
`HashGrid3`](https://docs.thi.ng/umbrella/geom-accel/classes/HashGrid2.html#queryNeighborhood)
|
|
70
|
+
from the [@thi.ng/geom-accel
|
|
71
|
+
package](https://github.com/thi-ng/umbrella/tree/develop/packages/geom-accel).
|
|
72
|
+
For cases where this isn't needed, the
|
|
73
|
+
[`noAccel`](https://docs.thi.ng/umbrella/boids/functions/noAccel.html) dummy
|
|
74
|
+
implementation of this interface can be used... In all cases, an acceleration
|
|
75
|
+
structure has to be provided to the boid ctor and factory functions
|
|
76
|
+
[`defBoid2()`](https://docs.thi.ng/umbrella/boids/functions/defBoid2.html) /
|
|
77
|
+
[`defBoid3()`](https://docs.thi.ng/umbrella/boids/functions/defBoid3.html).
|
|
26
78
|
|
|
27
79
|
## Status
|
|
28
80
|
|
|
@@ -50,13 +102,16 @@ For Node.js REPL:
|
|
|
50
102
|
const boids = await import("@thi.ng/boids");
|
|
51
103
|
```
|
|
52
104
|
|
|
53
|
-
Package sizes (brotli'd, pre-treeshake): ESM: 1.
|
|
105
|
+
Package sizes (brotli'd, pre-treeshake): ESM: 1.92 KB
|
|
54
106
|
|
|
55
107
|
## Dependencies
|
|
56
108
|
|
|
57
109
|
- [@thi.ng/api](https://github.com/thi-ng/umbrella/tree/develop/packages/api)
|
|
110
|
+
- [@thi.ng/checks](https://github.com/thi-ng/umbrella/tree/develop/packages/checks)
|
|
58
111
|
- [@thi.ng/distance](https://github.com/thi-ng/umbrella/tree/develop/packages/distance)
|
|
59
|
-
- [@thi.ng/geom-
|
|
112
|
+
- [@thi.ng/geom-closest-point](https://github.com/thi-ng/umbrella/tree/develop/packages/geom-closest-point)
|
|
113
|
+
- [@thi.ng/geom-resample](https://github.com/thi-ng/umbrella/tree/develop/packages/geom-resample)
|
|
114
|
+
- [@thi.ng/math](https://github.com/thi-ng/umbrella/tree/develop/packages/math)
|
|
60
115
|
- [@thi.ng/timestep](https://github.com/thi-ng/umbrella/tree/develop/packages/timestep)
|
|
61
116
|
- [@thi.ng/vectors](https://github.com/thi-ng/umbrella/tree/develop/packages/vectors)
|
|
62
117
|
|
|
@@ -93,4 +148,4 @@ If this project contributes to an academic publication, please cite it as:
|
|
|
93
148
|
|
|
94
149
|
## License
|
|
95
150
|
|
|
96
|
-
© 2023 Karsten Schmidt // Apache License 2.0
|
|
151
|
+
© 2023 - 2024 Karsten Schmidt // Apache License 2.0
|
package/accel.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { IBoidAccel } from "./api.js";
|
|
2
|
+
/**
|
|
3
|
+
* Dummy {@link IBoidAccel} implementation (i.e does no spatial indexing and
|
|
4
|
+
* always processes _all_ boids).
|
|
5
|
+
*
|
|
6
|
+
* @remarks
|
|
7
|
+
* Due to no incurred overhead for (re)constructing the acceleration structure,
|
|
8
|
+
* for low numbers of boids this can actually be more efficient than using a
|
|
9
|
+
* `HashGrid` (from thi.ng/geom-accel package).
|
|
10
|
+
*/
|
|
11
|
+
export declare const noAccel: () => IBoidAccel;
|
|
12
|
+
//# sourceMappingURL=accel.d.ts.map
|
package/accel.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
const noAccel = () => {
|
|
2
|
+
let boids;
|
|
3
|
+
return {
|
|
4
|
+
build($boids) {
|
|
5
|
+
boids = $boids;
|
|
6
|
+
},
|
|
7
|
+
queryNeighborhood(neighborhood) {
|
|
8
|
+
for (let b of boids)
|
|
9
|
+
neighborhood.consider(b.pos.curr, b);
|
|
10
|
+
return neighborhood;
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
export {
|
|
15
|
+
noAccel
|
|
16
|
+
};
|
package/api.d.ts
CHANGED
|
@@ -1,28 +1,45 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import type {
|
|
1
|
+
import type { Fn, Fn2, IDeref } from "@thi.ng/api";
|
|
2
|
+
import type { INeighborhood } from "@thi.ng/distance";
|
|
3
|
+
import type { ReadonlyVec, Vec } from "@thi.ng/vectors";
|
|
4
|
+
import type { Boid } from "./boid.js";
|
|
5
|
+
export type GlobalConstraint = Fn2<Vec, Boid, Vec>;
|
|
3
6
|
export interface BoidOpts {
|
|
4
|
-
constrain: FnU<Vec>;
|
|
5
|
-
maxSpeed: number;
|
|
6
|
-
maxForce: number;
|
|
7
7
|
/**
|
|
8
|
-
*
|
|
8
|
+
* Boid behaviors
|
|
9
|
+
*/
|
|
10
|
+
behaviors: IBoidBehavior[];
|
|
11
|
+
/**
|
|
12
|
+
* Behavior update function. Default: {@link blendedBehaviorUpdate}
|
|
9
13
|
*/
|
|
10
|
-
|
|
14
|
+
update?: Fn<Boid, Vec>;
|
|
11
15
|
/**
|
|
12
|
-
*
|
|
16
|
+
* Spatial acceleration structure to use for {@link Boid.neighbors} lookups.
|
|
13
17
|
*/
|
|
14
|
-
|
|
18
|
+
accel: IBoidAccel;
|
|
15
19
|
/**
|
|
16
|
-
*
|
|
20
|
+
* Position constraint. If used, the function MUST mutate the given vector
|
|
21
|
+
* (1st arg) and also return it.
|
|
17
22
|
*/
|
|
18
|
-
|
|
23
|
+
constrain?: GlobalConstraint;
|
|
19
24
|
/**
|
|
20
|
-
*
|
|
25
|
+
* Max speed (per second)
|
|
21
26
|
*/
|
|
22
|
-
|
|
27
|
+
maxSpeed: number;
|
|
23
28
|
/**
|
|
24
|
-
*
|
|
29
|
+
* Scale factor for applying any of the separation, alignment and cohesion
|
|
30
|
+
* forces.
|
|
31
|
+
*
|
|
32
|
+
* @defaultValue 1
|
|
25
33
|
*/
|
|
26
|
-
|
|
34
|
+
maxForce?: number;
|
|
35
|
+
}
|
|
36
|
+
export interface IBoidAccel {
|
|
37
|
+
build(boids: Boid[]): void;
|
|
38
|
+
queryNeighborhood<N extends INeighborhood<ReadonlyVec, Boid> & IDeref<Boid[]>>(neighborhood: N): N;
|
|
39
|
+
}
|
|
40
|
+
export interface IBoidBehavior {
|
|
41
|
+
weight(boid: Boid): number;
|
|
42
|
+
update(boid: Boid): ReadonlyVec;
|
|
27
43
|
}
|
|
44
|
+
export type ScalarOrField = number | Fn<Boid, number>;
|
|
28
45
|
//# sourceMappingURL=api.d.ts.map
|
package/api.js
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import {} from "@thi.ng/api";
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { __ensureFn } from "../internal/ensure.js";
|
|
2
|
+
const alignment = (maxDist, weight = 1) => {
|
|
3
|
+
const $maxDist = __ensureFn(maxDist);
|
|
4
|
+
const force = [];
|
|
5
|
+
return {
|
|
6
|
+
weight: __ensureFn(weight),
|
|
7
|
+
update: (boid) => {
|
|
8
|
+
const { add, setN } = boid.api;
|
|
9
|
+
const neighbors = boid.neighbors($maxDist(boid), boid.pos.curr);
|
|
10
|
+
const num = neighbors.length;
|
|
11
|
+
setN(force, 0);
|
|
12
|
+
for (let i = 0; i < num; i++) {
|
|
13
|
+
const n = neighbors[i];
|
|
14
|
+
if (n !== boid)
|
|
15
|
+
add(force, force, n.vel.curr);
|
|
16
|
+
}
|
|
17
|
+
return boid.computeSteer(force, num - 1);
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
export {
|
|
22
|
+
alignment
|
|
23
|
+
};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ReadonlyVec } from "@thi.ng/vectors";
|
|
2
|
+
import type { ScalarOrField, IBoidBehavior } from "../api.js";
|
|
3
|
+
export declare const attractPolyline: (points: ReadonlyVec[], closed: boolean, lookahead?: number, weight?: ScalarOrField) => IBoidBehavior;
|
|
4
|
+
//# sourceMappingURL=attraction.d.ts.map
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { closestPointPolyline } from "@thi.ng/geom-closest-point";
|
|
2
|
+
import { __ensureFn } from "../internal/ensure.js";
|
|
3
|
+
const attractPolyline = (points, closed, lookahead = 1, weight = 1) => {
|
|
4
|
+
const closest = [];
|
|
5
|
+
const pos = [];
|
|
6
|
+
return {
|
|
7
|
+
weight: __ensureFn(weight),
|
|
8
|
+
update: (boid) => {
|
|
9
|
+
const { add, normalize } = boid.api;
|
|
10
|
+
return closestPointPolyline(
|
|
11
|
+
lookahead !== 0 ? add(
|
|
12
|
+
pos,
|
|
13
|
+
normalize(pos, boid.vel.curr, lookahead),
|
|
14
|
+
boid.pos.curr
|
|
15
|
+
) : boid.pos.curr,
|
|
16
|
+
points,
|
|
17
|
+
closed,
|
|
18
|
+
closest
|
|
19
|
+
) ? boid.steerTowards(closest) : boid.api.ZERO;
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
export {
|
|
24
|
+
attractPolyline
|
|
25
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Fn } from "@thi.ng/api";
|
|
2
|
+
import type { ReadonlyVec } from "@thi.ng/vectors";
|
|
3
|
+
import type { ScalarOrField, IBoidBehavior } from "../api.js";
|
|
4
|
+
/**
|
|
5
|
+
* 2D only behavior. Takes a field function to be sampled, a sensor `lookahead`
|
|
6
|
+
* distance and `angle` between left/right sensors. The returned behavior steers
|
|
7
|
+
* an agent towards the local maxima of the field function based on a given
|
|
8
|
+
* agent's position and direction. Steers toward which ever sensor yields the
|
|
9
|
+
* greater value. If both sensors yield the same reading, the behavior is a
|
|
10
|
+
* no-op (returns a zero force vector).
|
|
11
|
+
*
|
|
12
|
+
* @param field
|
|
13
|
+
* @param lookahead
|
|
14
|
+
* @param angle
|
|
15
|
+
* @param weight
|
|
16
|
+
*/
|
|
17
|
+
export declare const braitenberg2: (field: Fn<ReadonlyVec, number>, lookahead: number, angle: number, weight?: ScalarOrField) => IBoidBehavior;
|
|
18
|
+
//# sourceMappingURL=braitenberg.d.ts.map
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { rotate } from "@thi.ng/vectors/rotate";
|
|
2
|
+
import { __ensureFn } from "../internal/ensure.js";
|
|
3
|
+
const braitenberg2 = (field, lookahead, angle, weight = 1) => {
|
|
4
|
+
const dir = [];
|
|
5
|
+
const left = [];
|
|
6
|
+
const right = [];
|
|
7
|
+
return {
|
|
8
|
+
weight: __ensureFn(weight),
|
|
9
|
+
update: (boid) => {
|
|
10
|
+
const { add, maddN, normalize } = boid.api;
|
|
11
|
+
normalize(dir, boid.vel.curr, lookahead);
|
|
12
|
+
const pos = boid.pos.curr;
|
|
13
|
+
const valC = field(pos);
|
|
14
|
+
const valL = field(add(left, rotate(left, dir, angle), pos));
|
|
15
|
+
const valR = field(add(right, rotate(right, dir, -angle), pos));
|
|
16
|
+
return valC > valL && valC > valR ? boid.steerTowards(maddN(left, boid.vel.curr, -1, pos)) : valL === valR ? boid.api.ZERO : boid.steerTowards(valL > valR ? left : right);
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
export {
|
|
21
|
+
braitenberg2
|
|
22
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { __ensureFn } from "../internal/ensure.js";
|
|
2
|
+
const cohesion = (maxDist, weight = 1) => {
|
|
3
|
+
const $maxDist = __ensureFn(maxDist);
|
|
4
|
+
const centroid = [];
|
|
5
|
+
return {
|
|
6
|
+
weight: __ensureFn(weight),
|
|
7
|
+
update: (boid) => {
|
|
8
|
+
const { add, mulN, setN } = boid.api;
|
|
9
|
+
const neighbors = boid.neighbors($maxDist(boid), boid.pos.curr);
|
|
10
|
+
const num = neighbors.length;
|
|
11
|
+
setN(centroid, 0);
|
|
12
|
+
for (let i = 0; i < num; i++) {
|
|
13
|
+
const n = neighbors[i];
|
|
14
|
+
if (n !== boid)
|
|
15
|
+
add(centroid, centroid, n.pos.curr);
|
|
16
|
+
}
|
|
17
|
+
return num > 1 ? boid.steerTowards(mulN(centroid, centroid, 1 / (num - 1))) : centroid;
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
export {
|
|
22
|
+
cohesion
|
|
23
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Fn, Nullable } from "@thi.ng/api";
|
|
2
|
+
import type { ReadonlyVec } from "@thi.ng/vectors";
|
|
3
|
+
import type { IBoidBehavior, ScalarOrField } from "../api.js";
|
|
4
|
+
import type { Boid } from "../boid.js";
|
|
5
|
+
/**
|
|
6
|
+
* Boid behavior which steers toward target positions sourced from user provided
|
|
7
|
+
* `target` fn. That `target` function will be called successively for each boid
|
|
8
|
+
* update. If it returns a point, it will be used as steer target until a new
|
|
9
|
+
* one is returned. If the function returns null/undefined the current target
|
|
10
|
+
* will be kept. The behavior is a no-op for boids outside the configured
|
|
11
|
+
* `radius` (around the target) and also has no effect until the target function
|
|
12
|
+
* returns its first point.
|
|
13
|
+
*
|
|
14
|
+
* @param target
|
|
15
|
+
* @param radius
|
|
16
|
+
* @param weight
|
|
17
|
+
*/
|
|
18
|
+
export declare const dynamicTarget: (target: Fn<Boid, Nullable<ReadonlyVec>>, radius?: ScalarOrField, weight?: ScalarOrField) => IBoidBehavior;
|
|
19
|
+
//# sourceMappingURL=dynamic.d.ts.map
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { __ensureFn } from "../internal/ensure.js";
|
|
2
|
+
const dynamicTarget = (target, radius = Infinity, weight = 1) => {
|
|
3
|
+
let currTarget;
|
|
4
|
+
const $radius = __ensureFn(radius);
|
|
5
|
+
const force = [];
|
|
6
|
+
return {
|
|
7
|
+
weight: __ensureFn(weight),
|
|
8
|
+
update: (boid) => {
|
|
9
|
+
currTarget = target(boid) || currTarget;
|
|
10
|
+
if (!currTarget)
|
|
11
|
+
return boid.api.ZERO;
|
|
12
|
+
const r = $radius(boid);
|
|
13
|
+
return boid.api.distSq(currTarget, boid.pos.curr) < r * r ? boid.steerTowards(currTarget, force) : boid.api.ZERO;
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
export {
|
|
18
|
+
dynamicTarget
|
|
19
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ReadonlyVec } from "@thi.ng/vectors";
|
|
2
|
+
import type { IBoidBehavior, ScalarOrField } from "../api.js";
|
|
3
|
+
/**
|
|
4
|
+
* Similar to {@link attractPolyline}, but forces steering along the path in the
|
|
5
|
+
* given order of points, using normalized `lookahead`. If `closed` is false,
|
|
6
|
+
* the behavior becomes a no-op for boids at the end of the path.
|
|
7
|
+
*
|
|
8
|
+
* @param points
|
|
9
|
+
* @param closed
|
|
10
|
+
* @param lookahead
|
|
11
|
+
* @param weight
|
|
12
|
+
*/
|
|
13
|
+
export declare const followPolyline: (points: ReadonlyVec[], closed: boolean, lookahead?: number, weight?: ScalarOrField) => IBoidBehavior;
|
|
14
|
+
//# sourceMappingURL=follow.d.ts.map
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Sampler } from "@thi.ng/geom-resample/sampler";
|
|
2
|
+
import { fract } from "@thi.ng/math/prec";
|
|
3
|
+
import { __ensureFn } from "../internal/ensure.js";
|
|
4
|
+
const followPolyline = (points, closed, lookahead = 0.01, weight = 1) => {
|
|
5
|
+
const sampler = new Sampler(points, closed);
|
|
6
|
+
return {
|
|
7
|
+
weight: __ensureFn(weight),
|
|
8
|
+
update: (boid) => {
|
|
9
|
+
const t = sampler.closestT(boid.pos.curr);
|
|
10
|
+
if (t === void 0 || !closed && t + lookahead > 1)
|
|
11
|
+
return boid.api.ZERO;
|
|
12
|
+
return boid.steerTowards(sampler.pointAt(fract(t + lookahead)));
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
export {
|
|
17
|
+
followPolyline
|
|
18
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
const mixedBehaviorUpdate = (boid) => {
|
|
2
|
+
const { maddN, zeroes } = boid.api;
|
|
3
|
+
const force = zeroes();
|
|
4
|
+
for (let behavior of boid.behaviors) {
|
|
5
|
+
const weight = behavior.weight(boid);
|
|
6
|
+
if (weight !== 0)
|
|
7
|
+
maddN(force, behavior.update(boid), weight, force);
|
|
8
|
+
}
|
|
9
|
+
return force;
|
|
10
|
+
};
|
|
11
|
+
export {
|
|
12
|
+
mixedBehaviorUpdate
|
|
13
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { closestPointPolyline } from "@thi.ng/geom-closest-point";
|
|
2
|
+
import { __ensureFn } from "../internal/ensure.js";
|
|
3
|
+
const followPath = (points, closed, weight = 1) => {
|
|
4
|
+
const closest = [];
|
|
5
|
+
const pos = [];
|
|
6
|
+
return {
|
|
7
|
+
weight: __ensureFn(weight),
|
|
8
|
+
update: (boid) => closestPointPolyline(
|
|
9
|
+
boid.api.add(pos, boid.pos.curr, boid.vel.curr),
|
|
10
|
+
points,
|
|
11
|
+
closed,
|
|
12
|
+
closest
|
|
13
|
+
) ? boid.steerTowards(closest) : boid.api.ZERO
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
export {
|
|
17
|
+
followPath
|
|
18
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { argSort } from "@thi.ng/arrays/arg-sort";
|
|
2
|
+
const probabilisticBehaviorUpdate = (boid) => {
|
|
3
|
+
const { maddN, magSq, zeroes } = boid.api;
|
|
4
|
+
const force = zeroes();
|
|
5
|
+
const behaviors = boid.behaviors;
|
|
6
|
+
const num = behaviors.length;
|
|
7
|
+
const weights = behaviors.map((b) => b.weight(boid));
|
|
8
|
+
const order = argSort(weights, (a, b) => b - a);
|
|
9
|
+
for (let i = 0; i < num; i++) {
|
|
10
|
+
const id = order[i];
|
|
11
|
+
const weight = weights[id];
|
|
12
|
+
if (Math.random() < weight) {
|
|
13
|
+
const f = behaviors[id].update(boid);
|
|
14
|
+
if (magSq(f) > 0) {
|
|
15
|
+
maddN(force, f, weight, force);
|
|
16
|
+
break;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return force;
|
|
21
|
+
};
|
|
22
|
+
export {
|
|
23
|
+
probabilisticBehaviorUpdate
|
|
24
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { __ensureFn } from "../internal/ensure.js";
|
|
2
|
+
const separation = (minDist, weight = 1) => {
|
|
3
|
+
const $minDist = __ensureFn(minDist);
|
|
4
|
+
const force = [];
|
|
5
|
+
const delta = [];
|
|
6
|
+
return {
|
|
7
|
+
weight: __ensureFn(weight),
|
|
8
|
+
update: (boid) => {
|
|
9
|
+
const { maddN, magSq, setN, sub } = boid.api;
|
|
10
|
+
const pos = boid.pos.curr;
|
|
11
|
+
const neighbors = boid.neighbors($minDist(boid), pos);
|
|
12
|
+
const num = neighbors.length;
|
|
13
|
+
let n;
|
|
14
|
+
setN(force, 0);
|
|
15
|
+
for (let i = 0; i < num; i++) {
|
|
16
|
+
n = neighbors[i];
|
|
17
|
+
if (n !== boid) {
|
|
18
|
+
sub(delta, pos, n.pos.curr);
|
|
19
|
+
maddN(force, delta, 1 / (magSq(delta) + 1e-6), force);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return boid.computeSteer(force, num - 1);
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
export {
|
|
27
|
+
separation
|
|
28
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
const blendedBehaviorUpdate = (boid) => {
|
|
2
|
+
const { maddN, zeroes } = boid.api;
|
|
3
|
+
const force = zeroes();
|
|
4
|
+
for (let behavior of boid.behaviors) {
|
|
5
|
+
const weight = behavior.weight(boid);
|
|
6
|
+
if (weight !== 0)
|
|
7
|
+
maddN(force, behavior.update(boid), weight, force);
|
|
8
|
+
}
|
|
9
|
+
return force;
|
|
10
|
+
};
|
|
11
|
+
export {
|
|
12
|
+
blendedBehaviorUpdate
|
|
13
|
+
};
|
package/boid.d.ts
CHANGED
|
@@ -1,22 +1,18 @@
|
|
|
1
1
|
import type { IDistance } from "@thi.ng/distance";
|
|
2
|
-
import type { AHashGrid, HashGrid2, HashGrid3 } from "@thi.ng/geom-accel/hash-grid";
|
|
3
2
|
import type { ITimeStep, ReadonlyTimeStep } from "@thi.ng/timestep";
|
|
4
3
|
import { VectorState } from "@thi.ng/timestep/state";
|
|
5
4
|
import type { ReadonlyVec, Vec, VecAPI } from "@thi.ng/vectors";
|
|
6
|
-
import type { BoidOpts } from "./api.js";
|
|
5
|
+
import type { BoidOpts, IBoidAccel, IBoidBehavior } from "./api.js";
|
|
7
6
|
import { Radial } from "./region.js";
|
|
8
7
|
export declare class Boid implements ITimeStep {
|
|
9
|
-
readonly accel: AHashGrid<Boid>;
|
|
10
|
-
readonly api: VecAPI;
|
|
11
8
|
pos: VectorState;
|
|
12
9
|
vel: VectorState;
|
|
13
|
-
|
|
10
|
+
api: VecAPI;
|
|
11
|
+
accel: IBoidAccel;
|
|
12
|
+
behaviors: IBoidBehavior[];
|
|
14
13
|
region: Radial<Boid>;
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
protected tmpAlign: Vec;
|
|
18
|
-
protected tmpCoh: Vec;
|
|
19
|
-
constructor(accel: AHashGrid<Boid>, api: VecAPI, distance: IDistance<ReadonlyVec>, pos: Vec, vel: Vec, opts: Partial<BoidOpts>);
|
|
14
|
+
opts: BoidOpts;
|
|
15
|
+
constructor(opts: BoidOpts, api: VecAPI, distance: IDistance<ReadonlyVec>, pos: Vec, vel: Vec);
|
|
20
16
|
/**
|
|
21
17
|
* Integration step of the thi.ng/timestep update cycle. See
|
|
22
18
|
* [`ITimeStep`](https://docs.thi.ng/umbrella/timestep/interfaces/ITimeStep.html)
|
|
@@ -47,12 +43,9 @@ export declare class Boid implements ITimeStep {
|
|
|
47
43
|
* @param pos
|
|
48
44
|
*/
|
|
49
45
|
neighbors(r: number, pos?: Vec): Boid[];
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
protected steerTowards(target: ReadonlyVec, out?: Vec): Vec;
|
|
54
|
-
protected computeSteer(steer: Vec, num: number): Vec;
|
|
55
|
-
protected limitSteer(steer: Vec): Vec;
|
|
46
|
+
steerTowards(target: ReadonlyVec, out?: Vec): Vec;
|
|
47
|
+
computeSteer(steer: Vec, num: number): Vec;
|
|
48
|
+
limitSteer(steer: Vec): Vec;
|
|
56
49
|
}
|
|
57
50
|
/**
|
|
58
51
|
* Returns a new {@link Boid} instance configured to use optimized 2D vector
|
|
@@ -63,7 +56,7 @@ export declare class Boid implements ITimeStep {
|
|
|
63
56
|
* @param vel
|
|
64
57
|
* @param opts
|
|
65
58
|
*/
|
|
66
|
-
export declare const defBoid2: (
|
|
59
|
+
export declare const defBoid2: (pos: Vec, vel: Vec, opts: BoidOpts) => Boid;
|
|
67
60
|
/**
|
|
68
61
|
* Returns a new {@link Boid} instance configured to use optimized 3D vector
|
|
69
62
|
* operations.
|
|
@@ -73,5 +66,5 @@ export declare const defBoid2: (accel: HashGrid2<Boid>, pos: Vec, vel: Vec, opts
|
|
|
73
66
|
* @param vel
|
|
74
67
|
* @param opts
|
|
75
68
|
*/
|
|
76
|
-
export declare const defBoid3: (
|
|
69
|
+
export declare const defBoid3: (pos: Vec, vel: Vec, opts: BoidOpts) => Boid;
|
|
77
70
|
//# sourceMappingURL=boid.d.ts.map
|
package/boid.js
CHANGED
|
@@ -2,59 +2,38 @@ import { identity } from "@thi.ng/api";
|
|
|
2
2
|
import { DIST_SQ2, DIST_SQ3 } from "@thi.ng/distance/squared";
|
|
3
3
|
import { VectorState, defVector } from "@thi.ng/timestep/state";
|
|
4
4
|
import { integrateAll, interpolateAll } from "@thi.ng/timestep/timestep";
|
|
5
|
-
import { addW4 } from "@thi.ng/vectors/addw";
|
|
6
5
|
import { VEC2 } from "@thi.ng/vectors/vec2-api";
|
|
7
6
|
import { VEC3 } from "@thi.ng/vectors/vec3-api";
|
|
7
|
+
import { blendedBehaviorUpdate } from "./behaviors/update.js";
|
|
8
8
|
import { Radial } from "./region.js";
|
|
9
9
|
class Boid {
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
pos;
|
|
11
|
+
vel;
|
|
12
|
+
api;
|
|
13
|
+
accel;
|
|
14
|
+
behaviors;
|
|
15
|
+
region;
|
|
16
|
+
opts;
|
|
17
|
+
constructor(opts, api, distance, pos, vel) {
|
|
12
18
|
this.api = api;
|
|
13
|
-
this.opts = {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
separation: 2,
|
|
20
|
-
alignment: 1,
|
|
21
|
-
cohesion: 1,
|
|
22
|
-
...opts
|
|
23
|
-
};
|
|
19
|
+
this.opts = { maxForce: 1, ...opts };
|
|
20
|
+
this.accel = this.opts.accel;
|
|
21
|
+
this.behaviors = this.opts.behaviors;
|
|
22
|
+
const update = this.opts.update || blendedBehaviorUpdate;
|
|
23
|
+
const constrain = this.opts.constrain || identity;
|
|
24
|
+
const { add, limit, maddN } = api;
|
|
24
25
|
this.vel = defVector(
|
|
25
26
|
api,
|
|
26
27
|
vel,
|
|
27
|
-
(vel2) =>
|
|
28
|
-
vel2,
|
|
29
|
-
addW4(
|
|
30
|
-
vel2,
|
|
31
|
-
vel2,
|
|
32
|
-
this.separate(),
|
|
33
|
-
this.align(),
|
|
34
|
-
this.cohesion(),
|
|
35
|
-
1,
|
|
36
|
-
this.opts.separation,
|
|
37
|
-
this.opts.alignment,
|
|
38
|
-
this.opts.cohesion
|
|
39
|
-
),
|
|
40
|
-
this.opts.maxSpeed
|
|
41
|
-
)
|
|
28
|
+
(vel2) => limit(vel2, add(vel2, vel2, update(this)), this.opts.maxSpeed)
|
|
42
29
|
);
|
|
43
30
|
this.pos = defVector(
|
|
44
31
|
api,
|
|
45
32
|
pos,
|
|
46
|
-
(pos2, dt) =>
|
|
33
|
+
(pos2, dt) => constrain(maddN(pos2, this.vel.curr, dt, pos2), this)
|
|
47
34
|
);
|
|
48
35
|
this.region = new Radial(distance, pos, 1);
|
|
49
36
|
}
|
|
50
|
-
pos;
|
|
51
|
-
vel;
|
|
52
|
-
opts;
|
|
53
|
-
region;
|
|
54
|
-
cachedNeighbors;
|
|
55
|
-
tmpSep = [];
|
|
56
|
-
tmpAlign = [];
|
|
57
|
-
tmpCoh = [];
|
|
58
37
|
/**
|
|
59
38
|
* Integration step of the thi.ng/timestep update cycle. See
|
|
60
39
|
* [`ITimeStep`](https://docs.thi.ng/umbrella/timestep/interfaces/ITimeStep.html)
|
|
@@ -63,10 +42,6 @@ class Boid {
|
|
|
63
42
|
* @param ctx
|
|
64
43
|
*/
|
|
65
44
|
integrate(dt, ctx) {
|
|
66
|
-
this.cachedNeighbors = this.neighbors(
|
|
67
|
-
this.opts.maxDist,
|
|
68
|
-
this.pos.value
|
|
69
|
-
);
|
|
70
45
|
integrateAll(dt, ctx, this.vel, this.pos);
|
|
71
46
|
}
|
|
72
47
|
/**
|
|
@@ -99,49 +74,6 @@ class Boid {
|
|
|
99
74
|
region.setRadius(r);
|
|
100
75
|
return this.accel.queryNeighborhood(region).deref();
|
|
101
76
|
}
|
|
102
|
-
separate() {
|
|
103
|
-
const { maddN, magSq, setN, sub } = this.api;
|
|
104
|
-
const pos = this.pos.value;
|
|
105
|
-
const neighbors = this.neighbors(this.opts.minDist);
|
|
106
|
-
const num = neighbors.length;
|
|
107
|
-
const delta = [];
|
|
108
|
-
const steer = setN(this.tmpSep, 0);
|
|
109
|
-
let n;
|
|
110
|
-
for (let i = 0; i < num; i++) {
|
|
111
|
-
n = neighbors[i];
|
|
112
|
-
if (n !== this) {
|
|
113
|
-
sub(delta, pos, n.pos.curr);
|
|
114
|
-
maddN(steer, delta, 1 / (magSq(delta) + 1e-6), steer);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
return this.computeSteer(steer, num);
|
|
118
|
-
}
|
|
119
|
-
align() {
|
|
120
|
-
const { add, setN } = this.api;
|
|
121
|
-
const neighbors = this.cachedNeighbors;
|
|
122
|
-
const num = neighbors.length;
|
|
123
|
-
const sum = setN(this.tmpAlign, 0);
|
|
124
|
-
let n;
|
|
125
|
-
for (let i = 0; i < num; i++) {
|
|
126
|
-
n = neighbors[i];
|
|
127
|
-
if (n !== this)
|
|
128
|
-
add(sum, sum, n.vel.curr);
|
|
129
|
-
}
|
|
130
|
-
return this.computeSteer(sum, num);
|
|
131
|
-
}
|
|
132
|
-
cohesion() {
|
|
133
|
-
const { add, mulN, setN } = this.api;
|
|
134
|
-
const neighbors = this.cachedNeighbors;
|
|
135
|
-
const num = neighbors.length;
|
|
136
|
-
const sum = setN(this.tmpCoh, 0);
|
|
137
|
-
let n;
|
|
138
|
-
for (let i = 0; i < num; i++) {
|
|
139
|
-
n = neighbors[i];
|
|
140
|
-
if (n !== this)
|
|
141
|
-
add(sum, sum, n.pos.curr);
|
|
142
|
-
}
|
|
143
|
-
return num > 0 ? this.steerTowards(mulN(sum, sum, 1 / num)) : sum;
|
|
144
|
-
}
|
|
145
77
|
steerTowards(target, out = target) {
|
|
146
78
|
return this.limitSteer(this.api.sub(out, target, this.pos.curr));
|
|
147
79
|
}
|
|
@@ -158,15 +90,15 @@ class Boid {
|
|
|
158
90
|
msubN(
|
|
159
91
|
steer,
|
|
160
92
|
steer,
|
|
161
|
-
this.opts.maxSpeed
|
|
93
|
+
this.opts.maxSpeed / Math.sqrt(m),
|
|
162
94
|
this.vel.curr
|
|
163
95
|
),
|
|
164
96
|
this.opts.maxForce
|
|
165
97
|
) : steer;
|
|
166
98
|
}
|
|
167
99
|
}
|
|
168
|
-
const defBoid2 = (
|
|
169
|
-
const defBoid3 = (
|
|
100
|
+
const defBoid2 = (pos, vel, opts) => new Boid(opts, VEC2, DIST_SQ2, pos, vel);
|
|
101
|
+
const defBoid3 = (pos, vel, opts) => new Boid(opts, VEC3, DIST_SQ3, pos, vel);
|
|
170
102
|
export {
|
|
171
103
|
Boid,
|
|
172
104
|
defBoid2,
|
package/constrain.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ReadonlyVec } from "@thi.ng/vectors";
|
|
2
|
+
import type { GlobalConstraint } from "./api.js";
|
|
3
|
+
export declare const clamp2: (min: ReadonlyVec, max: ReadonlyVec) => GlobalConstraint;
|
|
4
|
+
export declare const clamp3: (min: ReadonlyVec, max: ReadonlyVec) => GlobalConstraint;
|
|
5
|
+
export declare const wrap2: (min: ReadonlyVec, max: ReadonlyVec) => GlobalConstraint;
|
|
6
|
+
export declare const wrap3: (min: ReadonlyVec, max: ReadonlyVec) => GlobalConstraint;
|
|
7
|
+
//# sourceMappingURL=constrain.d.ts.map
|
package/constrain.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { wrapOnce } from "@thi.ng/math/interval";
|
|
2
|
+
import { clamp2 as $clamp2, clamp3 as $clamp3 } from "@thi.ng/vectors/clamp";
|
|
3
|
+
const clamp2 = (min, max) => (p) => $clamp2(p, p, min, max);
|
|
4
|
+
const clamp3 = (min, max) => (p) => $clamp3(p, p, min, max);
|
|
5
|
+
const wrap2 = (min, max) => (p, boid) => {
|
|
6
|
+
const [x, y] = p;
|
|
7
|
+
let wrap = false;
|
|
8
|
+
if (x < min[0] || x > max[0]) {
|
|
9
|
+
p[0] = wrapOnce(x, min[0], max[0]);
|
|
10
|
+
wrap = true;
|
|
11
|
+
}
|
|
12
|
+
if (y < min[1] || y > max[1]) {
|
|
13
|
+
p[1] = wrapOnce(y, min[1], max[1]);
|
|
14
|
+
wrap = true;
|
|
15
|
+
}
|
|
16
|
+
if (wrap)
|
|
17
|
+
boid.pos.reset(p);
|
|
18
|
+
return p;
|
|
19
|
+
};
|
|
20
|
+
const wrap3 = (min, max) => (p, boid) => {
|
|
21
|
+
let [x, y, z] = p;
|
|
22
|
+
let wrap = false;
|
|
23
|
+
if (x < min[0] || x > max[0]) {
|
|
24
|
+
p[0] = wrapOnce(x, min[0], max[0]);
|
|
25
|
+
wrap = true;
|
|
26
|
+
}
|
|
27
|
+
if (y < min[1] || y > max[1]) {
|
|
28
|
+
p[1] = wrapOnce(y, min[1], max[1]);
|
|
29
|
+
wrap = true;
|
|
30
|
+
}
|
|
31
|
+
if (z < min[2] || z > max[2]) {
|
|
32
|
+
p[2] = wrapOnce(z, min[2], max[2]);
|
|
33
|
+
wrap = true;
|
|
34
|
+
}
|
|
35
|
+
if (wrap)
|
|
36
|
+
boid.pos.reset(p);
|
|
37
|
+
return p;
|
|
38
|
+
};
|
|
39
|
+
export {
|
|
40
|
+
clamp2,
|
|
41
|
+
clamp3,
|
|
42
|
+
wrap2,
|
|
43
|
+
wrap3
|
|
44
|
+
};
|
package/flock.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { ITimeStep, ReadonlyTimeStep } from "@thi.ng/timestep";
|
|
2
|
+
import type { IBoidAccel } from "./api.js";
|
|
3
|
+
import type { Boid } from "./boid.js";
|
|
4
|
+
/**
|
|
5
|
+
* Returns a new {@link Flock} instance.
|
|
6
|
+
*
|
|
7
|
+
* @param accel
|
|
8
|
+
* @param boids
|
|
9
|
+
*/
|
|
10
|
+
export declare const defFlock: (accel: IBoidAccel, boids?: Boid[]) => Flock;
|
|
11
|
+
/**
|
|
12
|
+
* Convenience class for managing a number of boids (can be added
|
|
13
|
+
* dynamically) and their {@link IBoidAccel} structure. Like {@link Boid}
|
|
14
|
+
* itself, this class implements the `ITimeStep` interface too and the
|
|
15
|
+
* {@link ITimeStep.integrate} phase will automatically update the
|
|
16
|
+
* acceleration structure before integrating the boids.
|
|
17
|
+
*/
|
|
18
|
+
export declare class Flock implements ITimeStep {
|
|
19
|
+
accel: IBoidAccel;
|
|
20
|
+
boids: Boid[];
|
|
21
|
+
constructor(accel: IBoidAccel, boids?: Boid[]);
|
|
22
|
+
add(boid: Boid): void;
|
|
23
|
+
remove(boid: Boid): void;
|
|
24
|
+
integrate(dt: number, ctx: ReadonlyTimeStep): void;
|
|
25
|
+
interpolate(alpha: number, ctx: ReadonlyTimeStep): void;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=flock.d.ts.map
|
package/flock.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { integrateAll, interpolateAll } from "@thi.ng/timestep/timestep";
|
|
2
|
+
const defFlock = (accel, boids) => new Flock(accel, boids);
|
|
3
|
+
class Flock {
|
|
4
|
+
constructor(accel, boids = []) {
|
|
5
|
+
this.accel = accel;
|
|
6
|
+
this.boids = boids;
|
|
7
|
+
}
|
|
8
|
+
add(boid) {
|
|
9
|
+
this.boids.push(boid);
|
|
10
|
+
}
|
|
11
|
+
remove(boid) {
|
|
12
|
+
const idx = this.boids.indexOf(boid);
|
|
13
|
+
if (idx >= 0)
|
|
14
|
+
this.boids.splice(idx, 1);
|
|
15
|
+
}
|
|
16
|
+
integrate(dt, ctx) {
|
|
17
|
+
this.accel.build(this.boids);
|
|
18
|
+
integrateAll(dt, ctx, ...this.boids);
|
|
19
|
+
}
|
|
20
|
+
interpolate(alpha, ctx) {
|
|
21
|
+
interpolateAll(alpha, ctx, ...this.boids);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
export {
|
|
25
|
+
Flock,
|
|
26
|
+
defFlock
|
|
27
|
+
};
|
package/index.d.ts
CHANGED
|
@@ -1,4 +1,15 @@
|
|
|
1
|
+
export * from "./accel.js";
|
|
1
2
|
export * from "./api.js";
|
|
2
3
|
export * from "./boid.js";
|
|
4
|
+
export * from "./constrain.js";
|
|
5
|
+
export * from "./flock.js";
|
|
3
6
|
export * from "./region.js";
|
|
7
|
+
export * from "./behaviors/alignment.js";
|
|
8
|
+
export * from "./behaviors/attraction.js";
|
|
9
|
+
export * from "./behaviors/braitenberg.js";
|
|
10
|
+
export * from "./behaviors/cohesion.js";
|
|
11
|
+
export * from "./behaviors/dynamic.js";
|
|
12
|
+
export * from "./behaviors/follow.js";
|
|
13
|
+
export * from "./behaviors/separation.js";
|
|
14
|
+
export * from "./behaviors/update.js";
|
|
4
15
|
//# sourceMappingURL=index.d.ts.map
|
package/index.js
CHANGED
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
export * from "./accel.js";
|
|
1
2
|
export * from "./api.js";
|
|
2
3
|
export * from "./boid.js";
|
|
4
|
+
export * from "./constrain.js";
|
|
5
|
+
export * from "./flock.js";
|
|
3
6
|
export * from "./region.js";
|
|
7
|
+
export * from "./behaviors/alignment.js";
|
|
8
|
+
export * from "./behaviors/attraction.js";
|
|
9
|
+
export * from "./behaviors/braitenberg.js";
|
|
10
|
+
export * from "./behaviors/cohesion.js";
|
|
11
|
+
export * from "./behaviors/dynamic.js";
|
|
12
|
+
export * from "./behaviors/follow.js";
|
|
13
|
+
export * from "./behaviors/separation.js";
|
|
14
|
+
export * from "./behaviors/update.js";
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@thi.ng/boids",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "n-dimensional boids simulation with
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "n-dimensional boids simulation with modular behavior system",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"module": "./index.js",
|
|
7
7
|
"typings": "./index.d.ts",
|
|
@@ -35,11 +35,14 @@
|
|
|
35
35
|
"test": "bun test"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@thi.ng/api": "^8.9.
|
|
39
|
-
"@thi.ng/
|
|
40
|
-
"@thi.ng/
|
|
41
|
-
"@thi.ng/
|
|
42
|
-
"@thi.ng/
|
|
38
|
+
"@thi.ng/api": "^8.9.17",
|
|
39
|
+
"@thi.ng/checks": "^3.4.17",
|
|
40
|
+
"@thi.ng/distance": "^2.4.41",
|
|
41
|
+
"@thi.ng/geom-closest-point": "^2.1.95",
|
|
42
|
+
"@thi.ng/geom-resample": "^2.3.21",
|
|
43
|
+
"@thi.ng/math": "^5.7.12",
|
|
44
|
+
"@thi.ng/timestep": "^0.5.16",
|
|
45
|
+
"@thi.ng/vectors": "^7.9.0"
|
|
43
46
|
},
|
|
44
47
|
"devDependencies": {
|
|
45
48
|
"@microsoft/api-extractor": "^7.39.0",
|
|
@@ -52,12 +55,26 @@
|
|
|
52
55
|
"nd",
|
|
53
56
|
"2d",
|
|
54
57
|
"3d",
|
|
58
|
+
"acceleration",
|
|
59
|
+
"align",
|
|
60
|
+
"attractor",
|
|
55
61
|
"behavior",
|
|
62
|
+
"braitenberg",
|
|
56
63
|
"boids",
|
|
64
|
+
"constraint",
|
|
57
65
|
"flocking",
|
|
66
|
+
"follow",
|
|
67
|
+
"force",
|
|
68
|
+
"modular",
|
|
69
|
+
"path",
|
|
70
|
+
"physics",
|
|
71
|
+
"polyline",
|
|
72
|
+
"polygon",
|
|
58
73
|
"points",
|
|
74
|
+
"separation",
|
|
59
75
|
"simulation",
|
|
60
76
|
"spatial",
|
|
77
|
+
"steering",
|
|
61
78
|
"time",
|
|
62
79
|
"typescript",
|
|
63
80
|
"vector"
|
|
@@ -74,22 +91,57 @@
|
|
|
74
91
|
},
|
|
75
92
|
"files": [
|
|
76
93
|
"./*.js",
|
|
77
|
-
"./*.d.ts"
|
|
94
|
+
"./*.d.ts",
|
|
95
|
+
"behaviors",
|
|
96
|
+
"internal"
|
|
78
97
|
],
|
|
79
98
|
"exports": {
|
|
80
99
|
".": {
|
|
81
100
|
"default": "./index.js"
|
|
82
101
|
},
|
|
102
|
+
"./accel": {
|
|
103
|
+
"default": "./accel.js"
|
|
104
|
+
},
|
|
83
105
|
"./api": {
|
|
84
106
|
"default": "./api.js"
|
|
85
107
|
},
|
|
108
|
+
"./behaviors/alignment": {
|
|
109
|
+
"default": "./behaviors/alignment.js"
|
|
110
|
+
},
|
|
111
|
+
"./behaviors/attraction": {
|
|
112
|
+
"default": "./behaviors/attraction.js"
|
|
113
|
+
},
|
|
114
|
+
"./behaviors/braitenberg": {
|
|
115
|
+
"default": "./behaviors/braitenberg.js"
|
|
116
|
+
},
|
|
117
|
+
"./behaviors/cohesion": {
|
|
118
|
+
"default": "./behaviors/cohesion.js"
|
|
119
|
+
},
|
|
120
|
+
"./behaviors/dynamic": {
|
|
121
|
+
"default": "./behaviors/dynamic.js"
|
|
122
|
+
},
|
|
123
|
+
"./behaviors/follow": {
|
|
124
|
+
"default": "./behaviors/follow.js"
|
|
125
|
+
},
|
|
126
|
+
"./behaviors/separation": {
|
|
127
|
+
"default": "./behaviors/separation.js"
|
|
128
|
+
},
|
|
129
|
+
"./behaviors/update": {
|
|
130
|
+
"default": "./behaviors/update.js"
|
|
131
|
+
},
|
|
86
132
|
"./boid": {
|
|
87
133
|
"default": "./boid.js"
|
|
134
|
+
},
|
|
135
|
+
"./constrain": {
|
|
136
|
+
"default": "./constrain.js"
|
|
137
|
+
},
|
|
138
|
+
"./flock": {
|
|
139
|
+
"default": "./flock.js"
|
|
88
140
|
}
|
|
89
141
|
},
|
|
90
142
|
"thi.ng": {
|
|
91
143
|
"status": "alpha",
|
|
92
144
|
"year": 2023
|
|
93
145
|
},
|
|
94
|
-
"gitHead": "
|
|
146
|
+
"gitHead": "417b5a7ea7bd54a3b4f086fe0fc2ce8e8933c9b2\n"
|
|
95
147
|
}
|