@thi.ng/boids 1.0.78 → 1.1.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # Change Log
2
2
 
3
- - **Last updated**: 2025-04-30T12:52:32Z
3
+ - **Last updated**: 2025-05-08T15:13:02Z
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,21 @@ 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.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/boids@1.1.0) (2025-05-08)
15
+
16
+ #### 🚀 Features
17
+
18
+ - add optional amp param for behaviors ([74720a5](https://github.com/thi-ng/umbrella/commit/74720a5))
19
+ - update alignment(), cohesion(), separation() behaviors
20
+ - add optional `BoidOpts.id` ([965c2ad](https://github.com/thi-ng/umbrella/commit/965c2ad))
21
+
22
+ #### ♻️ Refactoring
23
+
24
+ - rename Boid internals ([8bdd9cd](https://github.com/thi-ng/umbrella/commit/8bdd9cd))
25
+ - rename `.computeSteer()` => `.averageForce()`
26
+ - rename `.limitSteer()` => `.limitForce()`
27
+ - add docs
28
+
14
29
  ### [1.0.34](https://github.com/thi-ng/umbrella/tree/@thi.ng/boids@1.0.34) (2024-06-21)
15
30
 
16
31
  #### ♻️ Refactoring
package/README.md CHANGED
@@ -111,7 +111,7 @@ For Node.js REPL:
111
111
  const boids = await import("@thi.ng/boids");
112
112
  ```
113
113
 
114
- Package sizes (brotli'd, pre-treeshake): ESM: 1.91 KB
114
+ Package sizes (brotli'd, pre-treeshake): ESM: 1.92 KB
115
115
 
116
116
  ## Dependencies
117
117
 
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
  */
@@ -1,3 +1,5 @@
1
+ import type { FnU2 } from "@thi.ng/api";
1
2
  import type { IBoidBehavior, ScalarOrField } from "../api.js";
2
- export declare const alignment: (maxDist: ScalarOrField, weight?: ScalarOrField) => IBoidBehavior;
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
@@ -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) add(force, force, n.vel.curr);
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.computeSteer(force, num - 1);
18
+ return boid.averageForce(force, num - 1);
17
19
  }
18
20
  };
19
21
  };
@@ -1,3 +1,5 @@
1
+ import type { FnU2 } from "@thi.ng/api";
1
2
  import type { IBoidBehavior, ScalarOrField } from "../api.js";
2
- export declare const cohesion: (maxDist: ScalarOrField, weight?: ScalarOrField) => IBoidBehavior;
3
+ import type { Boid } from "../boid.js";
4
+ export declare const cohesion: (maxDist: ScalarOrField, weight?: ScalarOrField, amp?: FnU2<Boid, number>) => IBoidBehavior;
3
5
  //# sourceMappingURL=cohesion.d.ts.map
@@ -1,17 +1,19 @@
1
1
  import { __ensureFn } from "../internal/ensure.js";
2
- const cohesion = (maxDist, weight = 1) => {
2
+ const cohesion = (maxDist, weight = 1, amp) => {
3
3
  const $maxDist = __ensureFn(maxDist);
4
4
  const centroid = [];
5
5
  return {
6
6
  weight: __ensureFn(weight),
7
7
  update: (boid) => {
8
- const { add, mulN, setN } = boid.api;
8
+ const { add, maddN, mulN, setN } = boid.api;
9
9
  const neighbors = boid.neighbors($maxDist(boid), boid.pos.curr);
10
10
  const num = neighbors.length;
11
11
  setN(centroid, 0);
12
12
  for (let i = 0; i < num; i++) {
13
13
  const n = neighbors[i];
14
- if (n !== boid) add(centroid, centroid, n.pos.curr);
14
+ if (n !== boid) {
15
+ amp ? maddN(centroid, n.pos.curr, amp(boid, n), centroid) : add(centroid, centroid, n.pos.curr);
16
+ }
15
17
  }
16
18
  return num > 1 ? boid.steerTowards(mulN(centroid, centroid, 1 / (num - 1))) : centroid;
17
19
  }
@@ -1,3 +1,16 @@
1
+ import type { FnU2 } from "@thi.ng/api";
1
2
  import type { IBoidBehavior, ScalarOrField } from "../api.js";
2
- export declare const separation: (minDist: ScalarOrField, weight?: ScalarOrField) => IBoidBehavior;
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
@@ -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(force, delta, 1 / (magSq(delta) + 1e-6), force);
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.computeSteer(force, num - 1);
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
- computeSteer(steer: Vec, num: number): Vec<number>;
48
- limitSteer(steer: Vec): Vec<number>;
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.limitSteer(this.api.sub(out, target, this.pos.curr));
77
+ return this.limitForce(this.api.sub(out, target, this.pos.curr));
78
78
  }
79
- computeSteer(steer, num) {
80
- return this.limitSteer(
81
- num > 0 ? this.api.mulN(steer, steer, 1 / num) : steer
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
- limitSteer(steer) {
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(steer);
99
+ const m = magSq(force);
87
100
  return m > 0 ? limit(
88
- steer,
101
+ force,
89
102
  msubN(
90
- steer,
91
- steer,
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
- ) : steer;
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.0.78",
3
+ "version": "1.1.0",
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": "21e1e46f087a0b8e53b7eafa38ac38a83596ef8f\n"
150
+ "gitHead": "8e61726779260744ea70bfe38438e755d6b53844\n"
151
151
  }