@thi.ng/boids 1.0.78 → 1.1.1
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 +22 -1
- package/README.md +1 -1
- package/api.d.ts +4 -0
- package/behaviors/alignment.d.ts +3 -1
- package/behaviors/alignment.js +6 -4
- package/behaviors/cohesion.d.ts +14 -1
- package/behaviors/cohesion.js +3 -2
- package/behaviors/separation.d.ts +14 -1
- package/behaviors/separation.js +8 -3
- package/boid.d.ts +15 -2
- package/boid.js +23 -10
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
-
- **Last updated**: 2025-
|
|
3
|
+
- **Last updated**: 2025-05-08T16:48:10Z
|
|
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.
|
|
@@ -11,6 +11,27 @@ See [Conventional Commits](https://conventionalcommits.org/) for commit guidelin
|
|
|
11
11
|
**Note:** Unlisted _patch_ versions only involve non-code or otherwise excluded changes
|
|
12
12
|
and/or version bumps of transitive dependencies.
|
|
13
13
|
|
|
14
|
+
### [1.1.1](https://github.com/thi-ng/umbrella/tree/@thi.ng/boids@1.1.1) (2025-05-08)
|
|
15
|
+
|
|
16
|
+
#### 🩹 Bug fixes
|
|
17
|
+
|
|
18
|
+
- update cohesion(), use filter predicate ([66d219b](https://github.com/thi-ng/umbrella/commit/66d219b))
|
|
19
|
+
|
|
20
|
+
## [1.1.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/boids@1.1.0) (2025-05-08)
|
|
21
|
+
|
|
22
|
+
#### 🚀 Features
|
|
23
|
+
|
|
24
|
+
- add optional amp param for behaviors ([74720a5](https://github.com/thi-ng/umbrella/commit/74720a5))
|
|
25
|
+
- update alignment(), cohesion(), separation() behaviors
|
|
26
|
+
- add optional `BoidOpts.id` ([965c2ad](https://github.com/thi-ng/umbrella/commit/965c2ad))
|
|
27
|
+
|
|
28
|
+
#### ♻️ Refactoring
|
|
29
|
+
|
|
30
|
+
- rename Boid internals ([8bdd9cd](https://github.com/thi-ng/umbrella/commit/8bdd9cd))
|
|
31
|
+
- rename `.computeSteer()` => `.averageForce()`
|
|
32
|
+
- rename `.limitSteer()` => `.limitForce()`
|
|
33
|
+
- add docs
|
|
34
|
+
|
|
14
35
|
### [1.0.34](https://github.com/thi-ng/umbrella/tree/@thi.ng/boids@1.0.34) (2024-06-21)
|
|
15
36
|
|
|
16
37
|
#### ♻️ Refactoring
|
package/README.md
CHANGED
package/api.d.ts
CHANGED
|
@@ -4,6 +4,10 @@ import type { ReadonlyVec, Vec } from "@thi.ng/vectors";
|
|
|
4
4
|
import type { Boid } from "./boid.js";
|
|
5
5
|
export type GlobalConstraint = Fn2<Vec, Boid, Vec>;
|
|
6
6
|
export interface BoidOpts {
|
|
7
|
+
/**
|
|
8
|
+
* Optional ID for this boid.
|
|
9
|
+
*/
|
|
10
|
+
id?: number;
|
|
7
11
|
/**
|
|
8
12
|
* Boid behaviors
|
|
9
13
|
*/
|
package/behaviors/alignment.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { FnU2 } from "@thi.ng/api";
|
|
1
2
|
import type { IBoidBehavior, ScalarOrField } from "../api.js";
|
|
2
|
-
|
|
3
|
+
import type { Boid } from "../boid.js";
|
|
4
|
+
export declare const alignment: (maxDist: ScalarOrField, weight?: ScalarOrField, amp?: FnU2<Boid, number>) => IBoidBehavior;
|
|
3
5
|
//# sourceMappingURL=alignment.d.ts.map
|
package/behaviors/alignment.js
CHANGED
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
import { __ensureFn } from "../internal/ensure.js";
|
|
2
|
-
const alignment = (maxDist, weight = 1) => {
|
|
2
|
+
const alignment = (maxDist, weight = 1, amp) => {
|
|
3
3
|
const $maxDist = __ensureFn(maxDist);
|
|
4
4
|
const force = [];
|
|
5
5
|
return {
|
|
6
6
|
weight: __ensureFn(weight),
|
|
7
7
|
update: (boid) => {
|
|
8
|
-
const { add, setN } = boid.api;
|
|
8
|
+
const { add, maddN, setN } = boid.api;
|
|
9
9
|
const neighbors = boid.neighbors($maxDist(boid), boid.pos.curr);
|
|
10
10
|
const num = neighbors.length;
|
|
11
11
|
setN(force, 0);
|
|
12
12
|
for (let i = 0; i < num; i++) {
|
|
13
13
|
const n = neighbors[i];
|
|
14
|
-
if (n !== boid)
|
|
14
|
+
if (n !== boid) {
|
|
15
|
+
amp ? maddN(force, n.vel.curr, amp(boid, n), force) : add(force, force, n.vel.curr);
|
|
16
|
+
}
|
|
15
17
|
}
|
|
16
|
-
return boid.
|
|
18
|
+
return boid.averageForce(force, num - 1);
|
|
17
19
|
}
|
|
18
20
|
};
|
|
19
21
|
};
|
package/behaviors/cohesion.d.ts
CHANGED
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
import type { Predicate2 } from "@thi.ng/api";
|
|
1
2
|
import type { IBoidBehavior, ScalarOrField } from "../api.js";
|
|
2
|
-
|
|
3
|
+
import type { Boid } from "../boid.js";
|
|
4
|
+
/**
|
|
5
|
+
* Cohesion behavior. Attempts to move boid towards centroid of neighboring
|
|
6
|
+
* boids within `maxDist`. The behavior itself can be weighted via `weight`.
|
|
7
|
+
* Neighbors can be filtered via optional `filter` predicate, which receives
|
|
8
|
+
* current boid as first arg and current neighbor as second. By default all
|
|
9
|
+
* neighbors are considered.
|
|
10
|
+
*
|
|
11
|
+
* @param maxDist
|
|
12
|
+
* @param weight
|
|
13
|
+
* @param pred
|
|
14
|
+
*/
|
|
15
|
+
export declare const cohesion: (maxDist: ScalarOrField, weight?: ScalarOrField, pred?: Predicate2<Boid>) => IBoidBehavior;
|
|
3
16
|
//# sourceMappingURL=cohesion.d.ts.map
|
package/behaviors/cohesion.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { __ensureFn } from "../internal/ensure.js";
|
|
2
|
-
const cohesion = (maxDist, weight = 1) => {
|
|
2
|
+
const cohesion = (maxDist, weight = 1, pred = () => true) => {
|
|
3
3
|
const $maxDist = __ensureFn(maxDist);
|
|
4
4
|
const centroid = [];
|
|
5
5
|
return {
|
|
@@ -11,7 +11,8 @@ const cohesion = (maxDist, weight = 1) => {
|
|
|
11
11
|
setN(centroid, 0);
|
|
12
12
|
for (let i = 0; i < num; i++) {
|
|
13
13
|
const n = neighbors[i];
|
|
14
|
-
if (n !== boid
|
|
14
|
+
if (n !== boid && pred(boid, n))
|
|
15
|
+
add(centroid, centroid, n.pos.curr);
|
|
15
16
|
}
|
|
16
17
|
return num > 1 ? boid.steerTowards(mulN(centroid, centroid, 1 / (num - 1))) : centroid;
|
|
17
18
|
}
|
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
import type { FnU2 } from "@thi.ng/api";
|
|
1
2
|
import type { IBoidBehavior, ScalarOrField } from "../api.js";
|
|
2
|
-
|
|
3
|
+
import type { Boid } from "../boid.js";
|
|
4
|
+
/**
|
|
5
|
+
* Separation behavior. Attempts to repel other boids closer the given
|
|
6
|
+
* `minDist`. The behavior itself can be weighted via `weight`. The force for
|
|
7
|
+
* resolving individual boid "collisions" can be amplified via optional `amp`
|
|
8
|
+
* function, which receives current boid as first arg and current neighbor as
|
|
9
|
+
* second. The default `amp` is uniformly 1.
|
|
10
|
+
*
|
|
11
|
+
* @param minDist
|
|
12
|
+
* @param weight
|
|
13
|
+
* @param amp
|
|
14
|
+
*/
|
|
15
|
+
export declare const separation: (minDist: ScalarOrField, weight?: ScalarOrField, amp?: FnU2<Boid, number>) => IBoidBehavior;
|
|
3
16
|
//# sourceMappingURL=separation.d.ts.map
|
package/behaviors/separation.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { __ensureFn } from "../internal/ensure.js";
|
|
2
|
-
const separation = (minDist, weight = 1) => {
|
|
2
|
+
const separation = (minDist, weight = 1, amp) => {
|
|
3
3
|
const $minDist = __ensureFn(minDist);
|
|
4
4
|
const force = [];
|
|
5
5
|
const delta = [];
|
|
@@ -16,10 +16,15 @@ const separation = (minDist, weight = 1) => {
|
|
|
16
16
|
n = neighbors[i];
|
|
17
17
|
if (n !== boid) {
|
|
18
18
|
sub(delta, pos, n.pos.curr);
|
|
19
|
-
maddN(
|
|
19
|
+
maddN(
|
|
20
|
+
force,
|
|
21
|
+
delta,
|
|
22
|
+
(amp?.(boid, n) ?? 1) / (magSq(delta) + 1e-6),
|
|
23
|
+
force
|
|
24
|
+
);
|
|
20
25
|
}
|
|
21
26
|
}
|
|
22
|
-
return boid.
|
|
27
|
+
return boid.averageForce(force, num - 1);
|
|
23
28
|
}
|
|
24
29
|
};
|
|
25
30
|
};
|
package/boid.d.ts
CHANGED
|
@@ -44,8 +44,21 @@ export declare class Boid implements ITimeStep {
|
|
|
44
44
|
*/
|
|
45
45
|
neighbors(r: number, pos?: Vec): Boid[];
|
|
46
46
|
steerTowards(target: ReadonlyVec, out?: Vec): Vec<number>;
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
/**
|
|
48
|
+
* Mutably divides given `force` by `num` (if > 0) and limits result via
|
|
49
|
+
* {@link Boid.limitForce}.
|
|
50
|
+
*
|
|
51
|
+
* @param force
|
|
52
|
+
* @param num
|
|
53
|
+
*/
|
|
54
|
+
averageForce(force: Vec, num: number): Vec<number>;
|
|
55
|
+
/**
|
|
56
|
+
* If force > 0, computes: `limit(normalize(force, maxSpeed) - vel, maxForce)`.
|
|
57
|
+
* Otherwise, returns input as is.
|
|
58
|
+
*
|
|
59
|
+
* @param force
|
|
60
|
+
*/
|
|
61
|
+
limitForce(force: Vec): Vec<number>;
|
|
49
62
|
}
|
|
50
63
|
/**
|
|
51
64
|
* Returns a new {@link Boid} instance configured to use optimized 2D vector
|
package/boid.js
CHANGED
|
@@ -74,26 +74,39 @@ class Boid {
|
|
|
74
74
|
return this.accel.queryNeighborhood(region).deref();
|
|
75
75
|
}
|
|
76
76
|
steerTowards(target, out = target) {
|
|
77
|
-
return this.
|
|
77
|
+
return this.limitForce(this.api.sub(out, target, this.pos.curr));
|
|
78
78
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
79
|
+
/**
|
|
80
|
+
* Mutably divides given `force` by `num` (if > 0) and limits result via
|
|
81
|
+
* {@link Boid.limitForce}.
|
|
82
|
+
*
|
|
83
|
+
* @param force
|
|
84
|
+
* @param num
|
|
85
|
+
*/
|
|
86
|
+
averageForce(force, num) {
|
|
87
|
+
return this.limitForce(
|
|
88
|
+
num > 0 ? this.api.mulN(force, force, 1 / num) : force
|
|
82
89
|
);
|
|
83
90
|
}
|
|
84
|
-
|
|
91
|
+
/**
|
|
92
|
+
* If force > 0, computes: `limit(normalize(force, maxSpeed) - vel, maxForce)`.
|
|
93
|
+
* Otherwise, returns input as is.
|
|
94
|
+
*
|
|
95
|
+
* @param force
|
|
96
|
+
*/
|
|
97
|
+
limitForce(force) {
|
|
85
98
|
const { limit, magSq, msubN } = this.api;
|
|
86
|
-
const m = magSq(
|
|
99
|
+
const m = magSq(force);
|
|
87
100
|
return m > 0 ? limit(
|
|
88
|
-
|
|
101
|
+
force,
|
|
89
102
|
msubN(
|
|
90
|
-
|
|
91
|
-
|
|
103
|
+
force,
|
|
104
|
+
force,
|
|
92
105
|
this.opts.maxSpeed / Math.sqrt(m),
|
|
93
106
|
this.vel.curr
|
|
94
107
|
),
|
|
95
108
|
this.opts.maxForce
|
|
96
|
-
) :
|
|
109
|
+
) : force;
|
|
97
110
|
}
|
|
98
111
|
}
|
|
99
112
|
const defBoid2 = (pos, vel, opts) => new Boid(opts, VEC2, DIST_SQ2, pos, vel);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@thi.ng/boids",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "n-dimensional boids simulation with modular behavior system",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"module": "./index.js",
|
|
@@ -147,5 +147,5 @@
|
|
|
147
147
|
"status": "alpha",
|
|
148
148
|
"year": 2023
|
|
149
149
|
},
|
|
150
|
-
"gitHead": "
|
|
150
|
+
"gitHead": "a28559ffe7cc0157c84b3c33a29f6b1f681e8c13\n"
|
|
151
151
|
}
|