@thi.ng/timestep 0.5.7 → 0.5.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-03T12:13:31Z
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
@@ -158,7 +158,7 @@ For Node.js REPL:
158
158
  const timestep = await import("@thi.ng/timestep");
159
159
  ```
160
160
 
161
- Package sizes (brotli'd, pre-treeshake): ESM: 973 bytes
161
+ Package sizes (brotli'd, pre-treeshake): ESM: 1.03 KB
162
162
 
163
163
  ## Dependencies
164
164
 
package/api.js CHANGED
@@ -1,10 +1,6 @@
1
- /**
2
- * Event ID for {@link TimeStep.addListener} to be notified of calls to
3
- * {@link TimeStep.update} (triggered at the very end of a frame update).
4
- */
5
- export const EVENT_FRAME = "frame";
6
- /**
7
- * Event ID for {@link TimeStep.addListener} to be notified of individual
8
- * {@link ITimeStep.integrate} iterations.
9
- */
10
- export const EVENT_SUBFRAME = "subframe";
1
+ const EVENT_FRAME = "frame";
2
+ const EVENT_SUBFRAME = "subframe";
3
+ export {
4
+ EVENT_FRAME,
5
+ EVENT_SUBFRAME
6
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thi.ng/timestep",
3
- "version": "0.5.7",
3
+ "version": "0.5.9",
4
4
  "description": "Deterministic fixed timestep simulation updates with state interpolation",
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,13 +35,13 @@
33
35
  "test": "bun test"
34
36
  },
35
37
  "dependencies": {
36
- "@thi.ng/api": "^8.9.10",
37
- "@thi.ng/math": "^5.7.5",
38
- "@thi.ng/vectors": "^7.8.7"
38
+ "@thi.ng/api": "^8.9.12",
39
+ "@thi.ng/math": "^5.7.7",
40
+ "@thi.ng/vectors": "^7.8.9"
39
41
  },
40
42
  "devDependencies": {
41
43
  "@microsoft/api-extractor": "^7.38.3",
42
- "@thi.ng/testament": "^0.4.3",
44
+ "esbuild": "^0.19.8",
43
45
  "rimraf": "^5.0.5",
44
46
  "tools": "^0.0.1",
45
47
  "typedoc": "^0.25.4",
@@ -90,5 +92,5 @@
90
92
  "status": "alpha",
91
93
  "year": 2023
92
94
  },
93
- "gitHead": "04d1de79f256d7a53c6b5fd157b37f49bc88e11d\n"
95
+ "gitHead": "5e7bafedfc3d53bc131469a28de31dd8e5b4a3ff\n"
94
96
  }
package/state.js CHANGED
@@ -1,117 +1,81 @@
1
1
  import { mix } from "@thi.ng/math/mix";
2
2
  import { mixN2, mixN3, mixN4 } from "@thi.ng/vectors/mixn";
3
3
  import { set2, set3, set4 } from "@thi.ng/vectors/set";
4
- export class NumericState {
5
- value;
6
- update;
7
- curr;
8
- prev;
9
- constructor(value, update) {
10
- this.value = value;
11
- this.update = update;
12
- this.reset(value);
13
- }
14
- deref() {
15
- return this.value;
16
- }
17
- /**
18
- * Sets {@link NumericState.prev}, {@link NumericState.curr} and
19
- * {@link NumericState.value} to given new value.
20
- *
21
- * @param value
22
- */
23
- reset(value) {
24
- this.prev = this.curr = this.value = value;
25
- }
26
- integrate(dt, ctx) {
27
- this.prev = this.curr;
28
- this.curr = this.update(this.curr, dt, ctx);
29
- }
30
- interpolate(alpha, _) {
31
- this.value = mix(this.prev, this.curr, alpha);
32
- }
4
+ class NumericState {
5
+ constructor(value, update) {
6
+ this.value = value;
7
+ this.update = update;
8
+ this.reset(value);
9
+ }
10
+ curr;
11
+ prev;
12
+ deref() {
13
+ return this.value;
14
+ }
15
+ /**
16
+ * Sets {@link NumericState.prev}, {@link NumericState.curr} and
17
+ * {@link NumericState.value} to given new value.
18
+ *
19
+ * @param value
20
+ */
21
+ reset(value) {
22
+ this.prev = this.curr = this.value = value;
23
+ }
24
+ integrate(dt, ctx) {
25
+ this.prev = this.curr;
26
+ this.curr = this.update(this.curr, dt, ctx);
27
+ }
28
+ interpolate(alpha, _) {
29
+ this.value = mix(this.prev, this.curr, alpha);
30
+ }
33
31
  }
34
- export class VectorState {
35
- value;
36
- update;
37
- prev;
38
- curr;
39
- setFn;
40
- mixFn;
41
- constructor(api, value, update) {
42
- this.value = value;
43
- this.update = update;
44
- this.setFn = api.set;
45
- this.mixFn = api.mixN;
46
- this.prev = this.setFn([], value);
47
- this.curr = this.setFn([], value);
48
- }
49
- deref() {
50
- return this.value;
51
- }
52
- /**
53
- * Copies given vector to {@link VectorState.prev}, {@link VectorState.curr}
54
- * and {@link VectorState.value}.
55
- *
56
- * @param value
57
- */
58
- reset(value) {
59
- const set = this.setFn;
60
- set(this.prev, value);
61
- set(this.curr, value);
62
- set(this.value, value);
63
- }
64
- integrate(dt, ctx) {
65
- this.setFn(this.prev, this.curr);
66
- this.update(this.curr, dt, ctx);
67
- }
68
- interpolate(alpha) {
69
- this.mixFn(this.value, this.prev, this.curr, alpha);
70
- }
32
+ class VectorState {
33
+ constructor(api, value, update) {
34
+ this.value = value;
35
+ this.update = update;
36
+ this.setFn = api.set;
37
+ this.mixFn = api.mixN;
38
+ this.prev = this.setFn([], value);
39
+ this.curr = this.setFn([], value);
40
+ }
41
+ prev;
42
+ curr;
43
+ setFn;
44
+ mixFn;
45
+ deref() {
46
+ return this.value;
47
+ }
48
+ /**
49
+ * Copies given vector to {@link VectorState.prev}, {@link VectorState.curr}
50
+ * and {@link VectorState.value}.
51
+ *
52
+ * @param value
53
+ */
54
+ reset(value) {
55
+ const set = this.setFn;
56
+ set(this.prev, value);
57
+ set(this.curr, value);
58
+ set(this.value, value);
59
+ }
60
+ integrate(dt, ctx) {
61
+ this.setFn(this.prev, this.curr);
62
+ this.update(this.curr, dt, ctx);
63
+ }
64
+ interpolate(alpha) {
65
+ this.mixFn(this.value, this.prev, this.curr, alpha);
66
+ }
71
67
  }
72
- /**
73
- * Returns a new {@link NumericState} wrapper for given value `x` and its update
74
- * function for use with {@link TimeStep.update}.
75
- *
76
- * @param x
77
- * @param update
78
- */
79
- export const defNumeric = (x, update) => new NumericState(x, update);
80
- /**
81
- * Returns a new {@link VectorState} wrapper for given vector `v` (arbitrary
82
- * length) and its update function for use with {@link TimeStep.update}. The
83
- * `api` object is used to provide dedicated vector ops used internally.
84
- *
85
- * @remarks
86
- * **IMPORTANT:** The `update` function MUST update the vector received as 1st
87
- * arg (which is {@link VectorState.curr}).
88
- *
89
- * Also see {@link defVector2}, {@link defVector3}, {@link defVector4} for
90
- * pre-configured versions.
91
- *
92
- * @param api
93
- * @param v
94
- * @param update
95
- */
96
- export const defVector = (api, v, update) => new VectorState(api, v, update);
97
- /**
98
- * 2D optimized version of {@link defVector}.
99
- *
100
- * @param v
101
- * @param update
102
- */
103
- export const defVector2 = (v, update) => new VectorState({ set: set2, mixN: mixN2 }, v, update);
104
- /**
105
- * 3D optimized version of {@link defVector}.
106
- *
107
- * @param v
108
- * @param update
109
- */
110
- export const defVector3 = (v, update) => new VectorState({ set: set3, mixN: mixN3 }, v, update);
111
- /**
112
- * 4D optimized version of {@link defVector}.
113
- *
114
- * @param v
115
- * @param update
116
- */
117
- export const defVector4 = (v, update) => new VectorState({ set: set4, mixN: mixN4 }, v, update);
68
+ const defNumeric = (x, update) => new NumericState(x, update);
69
+ const defVector = (api, v, update) => new VectorState(api, v, update);
70
+ const defVector2 = (v, update) => new VectorState({ set: set2, mixN: mixN2 }, v, update);
71
+ const defVector3 = (v, update) => new VectorState({ set: set3, mixN: mixN3 }, v, update);
72
+ const defVector4 = (v, update) => new VectorState({ set: set4, mixN: mixN4 }, v, update);
73
+ export {
74
+ NumericState,
75
+ VectorState,
76
+ defNumeric,
77
+ defVector,
78
+ defVector2,
79
+ defVector3,
80
+ defVector4
81
+ };
package/timestep.js CHANGED
@@ -1,109 +1,118 @@
1
- import { __decorate } from "tslib";
2
- import { INotifyMixin, } from "@thi.ng/api";
3
- import { EVENT_FRAME, EVENT_SUBFRAME, } from "./api.js";
4
- let TimeStep = class TimeStep {
5
- start;
6
- dt;
7
- maxFrameTime;
8
- scale;
9
- t = 0;
10
- current = 0;
11
- accumulator = 0;
12
- frame = 0;
13
- updates = 0;
14
- __eventFrame;
15
- __eventSubFrame;
16
- constructor(opts) {
17
- const $opts = {
18
- dt: 1 / 60,
19
- maxFrameTime: 1 / 4,
20
- startTime: 0,
21
- scale: 1e-3,
22
- ...opts,
23
- };
24
- this.dt = $opts.dt;
25
- this.maxFrameTime = $opts.maxFrameTime;
26
- this.scale = $opts.scale;
27
- this.start = $opts.startTime * this.scale;
28
- this.__eventFrame = Object.freeze({ id: EVENT_FRAME, target: this });
29
- this.__eventSubFrame = Object.freeze({
30
- id: EVENT_SUBFRAME,
31
- target: this,
32
- });
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __decorateClass = (decorators, target, key, kind) => {
4
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
5
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
6
+ if (decorator = decorators[i])
7
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
8
+ if (kind && result)
9
+ __defProp(target, key, result);
10
+ return result;
11
+ };
12
+ import {
13
+ INotifyMixin
14
+ } from "@thi.ng/api";
15
+ import {
16
+ EVENT_FRAME,
17
+ EVENT_SUBFRAME
18
+ } from "./api.js";
19
+ let TimeStep = class {
20
+ start;
21
+ dt;
22
+ maxFrameTime;
23
+ scale;
24
+ t = 0;
25
+ current = 0;
26
+ accumulator = 0;
27
+ frame = 0;
28
+ updates = 0;
29
+ __eventFrame;
30
+ __eventSubFrame;
31
+ constructor(opts) {
32
+ const $opts = {
33
+ dt: 1 / 60,
34
+ maxFrameTime: 1 / 4,
35
+ startTime: 0,
36
+ scale: 1e-3,
37
+ ...opts
38
+ };
39
+ this.dt = $opts.dt;
40
+ this.maxFrameTime = $opts.maxFrameTime;
41
+ this.scale = $opts.scale;
42
+ this.start = $opts.startTime * this.scale;
43
+ this.__eventFrame = Object.freeze({ id: EVENT_FRAME, target: this });
44
+ this.__eventSubFrame = Object.freeze({
45
+ id: EVENT_SUBFRAME,
46
+ target: this
47
+ });
48
+ }
49
+ // @ts-ignore mixin
50
+ // prettier-ignore
51
+ addListener(id, fn, scope) {
52
+ }
53
+ // @ts-ignore mixin
54
+ // prettier-ignore
55
+ removeListener(id, fn, scope) {
56
+ }
57
+ // @ts-ignore mixin
58
+ notify(event) {
59
+ }
60
+ /**
61
+ * Updates internal time to given new time `now` (given value will be scaled
62
+ * via {@link TimeStepOpts.scale}) and performs the required number of fixed
63
+ * timesteps to integrate and interpolate the given `state` values.
64
+ *
65
+ * @remarks
66
+ * If the scaled time difference since the last step is greater than
67
+ * {@link TimeStepOpts.maxFrameTime}, it will be limited to the latter.
68
+ *
69
+ * If `interpolate` is false, the {@link ITimeStep.interpolate} phase of the
70
+ * update cycle is skipped. This is useful when using this setup to simulate
71
+ * sub-steps (e.g. in XPBD) and only requiring the interpolation stage for
72
+ * the last step.
73
+ *
74
+ * @param now
75
+ * @param items
76
+ * @param interpolate
77
+ */
78
+ update(now, items, interpolate = true) {
79
+ now = now * this.scale - this.start;
80
+ this.accumulator += Math.min(now - this.current, this.maxFrameTime);
81
+ this.current = now;
82
+ const n = items.length;
83
+ const dt = this.dt;
84
+ while (this.accumulator >= dt) {
85
+ for (let i = 0; i < n; i++)
86
+ items[i].integrate(dt, this);
87
+ this.t += dt;
88
+ this.accumulator -= dt;
89
+ this.updates++;
90
+ this.notify(this.__eventSubFrame);
33
91
  }
34
- // @ts-ignore mixin
35
- // prettier-ignore
36
- addListener(id, fn, scope) { }
37
- // @ts-ignore mixin
38
- // prettier-ignore
39
- removeListener(id, fn, scope) { }
40
- // @ts-ignore mixin
41
- notify(event) { }
42
- /**
43
- * Updates internal time to given new time `now` (given value will be scaled
44
- * via {@link TimeStepOpts.scale}) and performs the required number of fixed
45
- * timesteps to integrate and interpolate the given `state` values.
46
- *
47
- * @remarks
48
- * If the scaled time difference since the last step is greater than
49
- * {@link TimeStepOpts.maxFrameTime}, it will be limited to the latter.
50
- *
51
- * If `interpolate` is false, the {@link ITimeStep.interpolate} phase of the
52
- * update cycle is skipped. This is useful when using this setup to simulate
53
- * sub-steps (e.g. in XPBD) and only requiring the interpolation stage for
54
- * the last step.
55
- *
56
- * @param now
57
- * @param items
58
- * @param interpolate
59
- */
60
- update(now, items, interpolate = true) {
61
- now = now * this.scale - this.start;
62
- this.accumulator += Math.min(now - this.current, this.maxFrameTime);
63
- this.current = now;
64
- const n = items.length;
65
- const dt = this.dt;
66
- while (this.accumulator >= dt) {
67
- for (let i = 0; i < n; i++)
68
- items[i].integrate(dt, this);
69
- this.t += dt;
70
- this.accumulator -= dt;
71
- this.updates++;
72
- this.notify(this.__eventSubFrame);
73
- }
74
- if (interpolate) {
75
- const alpha = this.accumulator / dt;
76
- for (let i = 0; i < n; i++)
77
- items[i].interpolate(alpha, this);
78
- }
79
- this.frame++;
80
- this.notify(this.__eventFrame);
92
+ if (interpolate) {
93
+ const alpha = this.accumulator / dt;
94
+ for (let i = 0; i < n; i++)
95
+ items[i].interpolate(alpha, this);
81
96
  }
97
+ this.frame++;
98
+ this.notify(this.__eventFrame);
99
+ }
82
100
  };
83
- TimeStep = __decorate([
84
- INotifyMixin
101
+ TimeStep = __decorateClass([
102
+ INotifyMixin
85
103
  ], TimeStep);
86
- export { TimeStep };
87
- export const defTimeStep = (opts) => new TimeStep(opts);
88
- /**
89
- * Calls {@link ITimeStep.integrate} for all given items (in given order).
90
- *
91
- * @param dt
92
- * @param ctx
93
- * @param items
94
- */
95
- export const integrateAll = (dt, ctx, ...items) => {
96
- for (let i = 0, n = items.length; i < n; i++)
97
- items[i].integrate(dt, ctx);
104
+ const defTimeStep = (opts) => new TimeStep(opts);
105
+ const integrateAll = (dt, ctx, ...items) => {
106
+ for (let i = 0, n = items.length; i < n; i++)
107
+ items[i].integrate(dt, ctx);
108
+ };
109
+ const interpolateAll = (alpha, ctx, ...items) => {
110
+ for (let i = 0, n = items.length; i < n; i++)
111
+ items[i].interpolate(alpha, ctx);
98
112
  };
99
- /**
100
- * Calls {@link ITimeStep.interpolate} for all given items (in given order).
101
- *
102
- * @param dt
103
- * @param ctx
104
- * @param items
105
- */
106
- export const interpolateAll = (alpha, ctx, ...items) => {
107
- for (let i = 0, n = items.length; i < n; i++)
108
- items[i].interpolate(alpha, ctx);
113
+ export {
114
+ TimeStep,
115
+ defTimeStep,
116
+ integrateAll,
117
+ interpolateAll
109
118
  };