@thi.ng/boids 0.1.8 → 0.1.9

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**: 2023-12-09T19:12:03Z
3
+ - **Last updated**: 2023-12-11T10:07:09Z
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.
package/README.md CHANGED
@@ -49,7 +49,7 @@ For Node.js REPL:
49
49
  const boids = await import("@thi.ng/boids");
50
50
  ```
51
51
 
52
- Package sizes (brotli'd, pre-treeshake): ESM: 1.14 KB
52
+ Package sizes (brotli'd, pre-treeshake): ESM: 1.13 KB
53
53
 
54
54
  ## Dependencies
55
55
 
package/boid.js CHANGED
@@ -6,150 +6,169 @@ import { addW4 } from "@thi.ng/vectors/addw";
6
6
  import { VEC2 } from "@thi.ng/vectors/vec2-api";
7
7
  import { VEC3 } from "@thi.ng/vectors/vec3-api";
8
8
  import { Radial } from "./region.js";
9
- export class Boid {
10
- accel;
11
- api;
12
- pos;
13
- vel;
14
- opts;
15
- region;
16
- cachedNeighbors;
17
- tmpSep = [];
18
- tmpAlign = [];
19
- tmpCoh = [];
20
- constructor(accel, api, distance, pos, vel, opts) {
21
- this.accel = accel;
22
- this.api = api;
23
- this.opts = {
24
- constrain: identity,
25
- maxSpeed: 10,
26
- maxForce: 1,
27
- minDist: 20,
28
- maxDist: 50,
29
- separation: 2,
30
- alignment: 1,
31
- cohesion: 1,
32
- ...opts,
33
- };
34
- this.vel = defVector(api, vel, (vel) => api.limit(vel, addW4(vel, vel, this.separate(), this.align(), this.cohesion(), 1, this.opts.separation, this.opts.alignment, this.opts.cohesion), this.opts.maxSpeed));
35
- this.pos = defVector(api, pos, (pos, dt) => this.opts.constrain(api.maddN(pos, this.vel.curr, dt, pos)));
36
- this.region = new Radial(distance, pos, 1);
9
+ class Boid {
10
+ constructor(accel, api, distance, pos, vel, opts) {
11
+ this.accel = accel;
12
+ this.api = api;
13
+ this.opts = {
14
+ constrain: identity,
15
+ maxSpeed: 10,
16
+ maxForce: 1,
17
+ minDist: 20,
18
+ maxDist: 50,
19
+ separation: 2,
20
+ alignment: 1,
21
+ cohesion: 1,
22
+ ...opts
23
+ };
24
+ this.vel = defVector(
25
+ api,
26
+ vel,
27
+ (vel2) => api.limit(
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
+ )
42
+ );
43
+ this.pos = defVector(
44
+ api,
45
+ pos,
46
+ (pos2, dt) => this.opts.constrain(api.maddN(pos2, this.vel.curr, dt, pos2))
47
+ );
48
+ this.region = new Radial(distance, pos, 1);
49
+ }
50
+ pos;
51
+ vel;
52
+ opts;
53
+ region;
54
+ cachedNeighbors;
55
+ tmpSep = [];
56
+ tmpAlign = [];
57
+ tmpCoh = [];
58
+ /**
59
+ * Integration step of the thi.ng/timestep update cycle. See
60
+ * [`ITimeStep`](https://docs.thi.ng/umbrella/timestep/interfaces/ITimeStep.html)
61
+ *
62
+ * @param dt
63
+ * @param ctx
64
+ */
65
+ integrate(dt, ctx) {
66
+ this.cachedNeighbors = this.neighbors(
67
+ this.opts.maxDist,
68
+ this.pos.value
69
+ );
70
+ integrateAll(dt, ctx, this.vel, this.pos);
71
+ }
72
+ /**
73
+ * Interplation step of the thi.ng/timestep update cycle. See
74
+ * [`ITimeStep`](https://docs.thi.ng/umbrella/timestep/interfaces/ITimeStep.html)
75
+ *
76
+ * @param dt
77
+ * @param ctx
78
+ */
79
+ interpolate(alpha, ctx) {
80
+ interpolateAll(alpha, ctx, this.vel, this.pos);
81
+ }
82
+ /**
83
+ * Queries the spatial index for other boids in the current region, or if
84
+ * `pos` is given also moves the search region to new position before
85
+ * querying.
86
+ *
87
+ * @remarks
88
+ * IMPORTANT: The returned array will always contain the current boid itself
89
+ * too. Filtering has been left out here for performance reasons and is left
90
+ * to downstream code.
91
+ *
92
+ * @param r
93
+ * @param pos
94
+ */
95
+ neighbors(r, pos) {
96
+ const region = this.region;
97
+ if (pos)
98
+ region.target = pos;
99
+ region.setRadius(r);
100
+ return this.accel.queryNeighborhood(region).deref();
101
+ }
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
+ }
37
116
  }
38
- /**
39
- * Integration step of the thi.ng/timestep update cycle. See
40
- * [`ITimeStep`](https://docs.thi.ng/umbrella/timestep/interfaces/ITimeStep.html)
41
- *
42
- * @param dt
43
- * @param ctx
44
- */
45
- integrate(dt, ctx) {
46
- this.cachedNeighbors = this.neighbors(this.opts.maxDist, this.pos.value);
47
- integrateAll(dt, ctx, this.vel, this.pos);
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);
48
129
  }
49
- /**
50
- * Interplation step of the thi.ng/timestep update cycle. See
51
- * [`ITimeStep`](https://docs.thi.ng/umbrella/timestep/interfaces/ITimeStep.html)
52
- *
53
- * @param dt
54
- * @param ctx
55
- */
56
- interpolate(alpha, ctx) {
57
- interpolateAll(alpha, ctx, this.vel, this.pos);
58
- }
59
- /**
60
- * Queries the spatial index for other boids in the current region, or if
61
- * `pos` is given also moves the search region to new position before
62
- * querying.
63
- *
64
- * @remarks
65
- * IMPORTANT: The returned array will always contain the current boid itself
66
- * too. Filtering has been left out here for performance reasons and is left
67
- * to downstream code.
68
- *
69
- * @param r
70
- * @param pos
71
- */
72
- neighbors(r, pos) {
73
- const region = this.region;
74
- if (pos)
75
- region.target = pos;
76
- region.setRadius(r);
77
- return this.accel.queryNeighborhood(region).deref();
78
- }
79
- separate() {
80
- const { maddN, magSq, setN, sub } = this.api;
81
- const pos = this.pos.value;
82
- const neighbors = this.neighbors(this.opts.minDist);
83
- const num = neighbors.length;
84
- const delta = [];
85
- const steer = setN(this.tmpSep, 0);
86
- let n;
87
- for (let i = 0; i < num; i++) {
88
- n = neighbors[i];
89
- if (n !== this) {
90
- sub(delta, pos, n.pos.curr);
91
- maddN(steer, delta, 1 / (magSq(delta) + 1e-6), steer);
92
- }
93
- }
94
- return this.computeSteer(steer, num);
95
- }
96
- align() {
97
- const { add, setN } = this.api;
98
- const neighbors = this.cachedNeighbors;
99
- const num = neighbors.length;
100
- const sum = setN(this.tmpAlign, 0);
101
- let n;
102
- for (let i = 0; i < num; i++) {
103
- n = neighbors[i];
104
- if (n !== this)
105
- add(sum, sum, n.vel.curr);
106
- }
107
- return this.computeSteer(sum, num);
108
- }
109
- cohesion() {
110
- const { add, mulN, setN } = this.api;
111
- const neighbors = this.cachedNeighbors;
112
- const num = neighbors.length;
113
- const sum = setN(this.tmpCoh, 0);
114
- let n;
115
- for (let i = 0; i < num; i++) {
116
- n = neighbors[i];
117
- if (n !== this)
118
- add(sum, sum, n.pos.curr);
119
- }
120
- return num > 0 ? this.steerTowards(mulN(sum, sum, 1 / num)) : sum;
121
- }
122
- steerTowards(target, out = target) {
123
- return this.limitSteer(this.api.sub(out, target, this.pos.curr));
124
- }
125
- computeSteer(steer, num) {
126
- return this.limitSteer(num > 0 ? this.api.mulN(steer, steer, 1 / num) : steer);
127
- }
128
- limitSteer(steer) {
129
- const { limit, magSq, msubN } = this.api;
130
- const m = magSq(steer);
131
- return m > 0
132
- ? limit(steer, msubN(steer, steer, this.opts.maxSpeed ** 2 / m, this.vel.curr), this.opts.maxForce)
133
- : steer;
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);
134
142
  }
143
+ return num > 0 ? this.steerTowards(mulN(sum, sum, 1 / num)) : sum;
144
+ }
145
+ steerTowards(target, out = target) {
146
+ return this.limitSteer(this.api.sub(out, target, this.pos.curr));
147
+ }
148
+ computeSteer(steer, num) {
149
+ return this.limitSteer(
150
+ num > 0 ? this.api.mulN(steer, steer, 1 / num) : steer
151
+ );
152
+ }
153
+ limitSteer(steer) {
154
+ const { limit, magSq, msubN } = this.api;
155
+ const m = magSq(steer);
156
+ return m > 0 ? limit(
157
+ steer,
158
+ msubN(
159
+ steer,
160
+ steer,
161
+ this.opts.maxSpeed ** 2 / m,
162
+ this.vel.curr
163
+ ),
164
+ this.opts.maxForce
165
+ ) : steer;
166
+ }
135
167
  }
136
- /**
137
- * Returns a new {@link Boid} instance configured to use optimized 2D vector
138
- * operations.
139
- *
140
- * @param accel
141
- * @param pos
142
- * @param vel
143
- * @param opts
144
- */
145
- export const defBoid2 = (accel, pos, vel, opts) => new Boid(accel, VEC2, DIST_SQ2, pos, vel, opts);
146
- /**
147
- * Returns a new {@link Boid} instance configured to use optimized 3D vector
148
- * operations.
149
- *
150
- * @param accel
151
- * @param pos
152
- * @param vel
153
- * @param opts
154
- */
155
- export const defBoid3 = (accel, pos, vel, opts) => new Boid(accel, VEC3, DIST_SQ3, pos, vel, opts);
168
+ const defBoid2 = (accel, pos, vel, opts) => new Boid(accel, VEC2, DIST_SQ2, pos, vel, opts);
169
+ const defBoid3 = (accel, pos, vel, opts) => new Boid(accel, VEC3, DIST_SQ3, pos, vel, opts);
170
+ export {
171
+ Boid,
172
+ defBoid2,
173
+ defBoid3
174
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thi.ng/boids",
3
- "version": "0.1.8",
3
+ "version": "0.1.9",
4
4
  "description": "n-dimensional boids simulation with highly configurable behaviors",
5
5
  "type": "module",
6
6
  "module": "./index.js",
@@ -24,7 +24,9 @@
24
24
  "author": "Karsten Schmidt (https://thi.ng)",
25
25
  "license": "Apache-2.0",
26
26
  "scripts": {
27
- "build": "yarn clean && tsc --declaration",
27
+ "build": "yarn build:esbuild && yarn build:decl",
28
+ "build:decl": "tsc --declaration --emitDeclarationOnly",
29
+ "build:esbuild": "esbuild --format=esm --platform=neutral --target=es2022 --tsconfig=tsconfig.json --outdir=. src/**/*.ts",
28
30
  "clean": "rimraf --glob '*.js' '*.d.ts' '*.map' doc",
29
31
  "doc": "typedoc --excludePrivate --excludeInternal --out doc src/index.ts",
30
32
  "doc:ae": "mkdir -p .ae/doc .ae/temp && api-extractor run --local --verbose",
@@ -33,14 +35,15 @@
33
35
  "test": "bun test"
34
36
  },
35
37
  "dependencies": {
36
- "@thi.ng/api": "^8.9.11",
37
- "@thi.ng/distance": "^2.4.33",
38
- "@thi.ng/geom-accel": "^3.5.33",
39
- "@thi.ng/timestep": "^0.5.8",
40
- "@thi.ng/vectors": "^7.8.8"
38
+ "@thi.ng/api": "^8.9.12",
39
+ "@thi.ng/distance": "^2.4.34",
40
+ "@thi.ng/geom-accel": "^3.5.34",
41
+ "@thi.ng/timestep": "^0.5.9",
42
+ "@thi.ng/vectors": "^7.8.9"
41
43
  },
42
44
  "devDependencies": {
43
45
  "@microsoft/api-extractor": "^7.38.3",
46
+ "esbuild": "^0.19.8",
44
47
  "rimraf": "^5.0.5",
45
48
  "tools": "^0.0.1",
46
49
  "typedoc": "^0.25.4",
@@ -89,5 +92,5 @@
89
92
  "status": "alpha",
90
93
  "year": 2023
91
94
  },
92
- "gitHead": "25f2ac8ff795a432a930119661b364d4d93b59a0\n"
95
+ "gitHead": "5e7bafedfc3d53bc131469a28de31dd8e5b4a3ff\n"
93
96
  }
package/region.js CHANGED
@@ -1,44 +1,37 @@
1
- // thing:no-export
2
1
  import {} from "@thi.ng/api";
3
- /**
4
- * In principle the same as eponymous class in thi.ng/distance, but with several
5
- * small optimizations for this package.
6
- *
7
- * @internal
8
- */
9
- export class Radial {
10
- dist;
11
- target;
12
- radius;
13
- _r;
14
- _items = [];
15
- constructor(dist, target, radius = Infinity) {
16
- this.dist = dist;
17
- this.target = target;
18
- this.radius = radius;
19
- this.setRadius(radius);
20
- }
21
- deref() {
22
- return this._items;
23
- }
24
- reset() {
25
- this._items.length = 0;
26
- return this;
27
- }
28
- setRadius(r) {
29
- this.radius = Math.max(0, r);
30
- this._r = this.dist.to(this.radius);
31
- this.reset();
32
- }
33
- includesDistance(d, eucledian = true) {
34
- return (eucledian ? this.dist.to(d) : d) <= this._r;
35
- }
36
- includesPosition(pos) {
37
- return this.dist.metric(this.target, pos) <= this._r;
38
- }
39
- consider(pos, val) {
40
- const d = this.dist.metric(this.target, pos);
41
- d <= this._r && this._items.push(val);
42
- return d;
43
- }
2
+ class Radial {
3
+ constructor(dist, target, radius = Infinity) {
4
+ this.dist = dist;
5
+ this.target = target;
6
+ this.radius = radius;
7
+ this.setRadius(radius);
8
+ }
9
+ _r;
10
+ _items = [];
11
+ deref() {
12
+ return this._items;
13
+ }
14
+ reset() {
15
+ this._items.length = 0;
16
+ return this;
17
+ }
18
+ setRadius(r) {
19
+ this.radius = Math.max(0, r);
20
+ this._r = this.dist.to(this.radius);
21
+ this.reset();
22
+ }
23
+ includesDistance(d, eucledian = true) {
24
+ return (eucledian ? this.dist.to(d) : d) <= this._r;
25
+ }
26
+ includesPosition(pos) {
27
+ return this.dist.metric(this.target, pos) <= this._r;
28
+ }
29
+ consider(pos, val) {
30
+ const d = this.dist.metric(this.target, pos);
31
+ d <= this._r && this._items.push(val);
32
+ return d;
33
+ }
44
34
  }
35
+ export {
36
+ Radial
37
+ };