murow 0.0.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.
Files changed (103) hide show
  1. package/README.md +61 -0
  2. package/dist/core/binary-codec/binary-codec.d.ts +159 -0
  3. package/dist/core/binary-codec/binary-codec.js +336 -0
  4. package/dist/core/binary-codec/index.d.ts +1 -0
  5. package/dist/core/binary-codec/index.js +1 -0
  6. package/dist/core/events/event-system.d.ts +71 -0
  7. package/dist/core/events/event-system.js +88 -0
  8. package/dist/core/events/index.d.ts +1 -0
  9. package/dist/core/events/index.js +1 -0
  10. package/dist/core/fixed-ticker/fixed-ticker.d.ts +105 -0
  11. package/dist/core/fixed-ticker/fixed-ticker.js +91 -0
  12. package/dist/core/fixed-ticker/index.d.ts +1 -0
  13. package/dist/core/fixed-ticker/index.js +1 -0
  14. package/dist/core/generate-id/generate-id.d.ts +21 -0
  15. package/dist/core/generate-id/generate-id.js +25 -0
  16. package/dist/core/generate-id/index.d.ts +1 -0
  17. package/dist/core/generate-id/index.js +1 -0
  18. package/dist/core/index.d.ts +8 -0
  19. package/dist/core/index.js +8 -0
  20. package/dist/core/lerp/index.d.ts +1 -0
  21. package/dist/core/lerp/index.js +1 -0
  22. package/dist/core/lerp/lerp.d.ts +40 -0
  23. package/dist/core/lerp/lerp.js +42 -0
  24. package/dist/core/navmesh/index.d.ts +1 -0
  25. package/dist/core/navmesh/index.js +1 -0
  26. package/dist/core/navmesh/navmesh.d.ts +116 -0
  27. package/dist/core/navmesh/navmesh.js +666 -0
  28. package/dist/core/pooled-codec/index.d.ts +1 -0
  29. package/dist/core/pooled-codec/index.js +1 -0
  30. package/dist/core/pooled-codec/pooled-codec.d.ts +140 -0
  31. package/dist/core/pooled-codec/pooled-codec.js +213 -0
  32. package/dist/core/prediction/index.d.ts +1 -0
  33. package/dist/core/prediction/index.js +1 -0
  34. package/dist/core/prediction/prediction.d.ts +64 -0
  35. package/dist/core/prediction/prediction.js +90 -0
  36. package/dist/core.esm.js +1 -0
  37. package/dist/core.js +1 -0
  38. package/dist/index.d.ts +16 -0
  39. package/dist/index.js +18 -0
  40. package/dist/protocol/index.d.ts +43 -0
  41. package/dist/protocol/index.js +43 -0
  42. package/dist/protocol/intent/index.d.ts +39 -0
  43. package/dist/protocol/intent/index.js +38 -0
  44. package/dist/protocol/intent/intent-registry.d.ts +54 -0
  45. package/dist/protocol/intent/intent-registry.js +73 -0
  46. package/dist/protocol/intent/intent.d.ts +12 -0
  47. package/dist/protocol/intent/intent.js +1 -0
  48. package/dist/protocol/snapshot/index.d.ts +44 -0
  49. package/dist/protocol/snapshot/index.js +43 -0
  50. package/dist/protocol/snapshot/snapshot-codec.d.ts +48 -0
  51. package/dist/protocol/snapshot/snapshot-codec.js +56 -0
  52. package/dist/protocol/snapshot/snapshot-registry.d.ts +100 -0
  53. package/dist/protocol/snapshot/snapshot-registry.js +136 -0
  54. package/dist/protocol/snapshot/snapshot.d.ts +19 -0
  55. package/dist/protocol/snapshot/snapshot.js +30 -0
  56. package/package.json +54 -0
  57. package/src/core/binary-codec/README.md +60 -0
  58. package/src/core/binary-codec/binary-codec.test.ts +300 -0
  59. package/src/core/binary-codec/binary-codec.ts +430 -0
  60. package/src/core/binary-codec/index.ts +1 -0
  61. package/src/core/events/README.md +47 -0
  62. package/src/core/events/event-system.test.ts +243 -0
  63. package/src/core/events/event-system.ts +140 -0
  64. package/src/core/events/index.ts +1 -0
  65. package/src/core/fixed-ticker/README.md +77 -0
  66. package/src/core/fixed-ticker/fixed-ticker.test.ts +151 -0
  67. package/src/core/fixed-ticker/fixed-ticker.ts +158 -0
  68. package/src/core/fixed-ticker/index.ts +1 -0
  69. package/src/core/generate-id/README.md +18 -0
  70. package/src/core/generate-id/generate-id.test.ts +79 -0
  71. package/src/core/generate-id/generate-id.ts +37 -0
  72. package/src/core/generate-id/index.ts +1 -0
  73. package/src/core/index.ts +8 -0
  74. package/src/core/lerp/README.md +79 -0
  75. package/src/core/lerp/index.ts +1 -0
  76. package/src/core/lerp/lerp.test.ts +90 -0
  77. package/src/core/lerp/lerp.ts +42 -0
  78. package/src/core/navmesh/README.md +124 -0
  79. package/src/core/navmesh/index.ts +1 -0
  80. package/src/core/navmesh/navmesh.test.ts +344 -0
  81. package/src/core/navmesh/navmesh.ts +850 -0
  82. package/src/core/pooled-codec/README.md +70 -0
  83. package/src/core/pooled-codec/index.ts +1 -0
  84. package/src/core/pooled-codec/pooled-codec.test.ts +349 -0
  85. package/src/core/pooled-codec/pooled-codec.ts +239 -0
  86. package/src/core/prediction/README.md +64 -0
  87. package/src/core/prediction/index.ts +1 -0
  88. package/src/core/prediction/prediction.test.ts +422 -0
  89. package/src/core/prediction/prediction.ts +101 -0
  90. package/src/index.ts +20 -0
  91. package/src/protocol/README.md +310 -0
  92. package/src/protocol/index.ts +44 -0
  93. package/src/protocol/intent/index.ts +40 -0
  94. package/src/protocol/intent/intent-registry.test.ts +237 -0
  95. package/src/protocol/intent/intent-registry.ts +88 -0
  96. package/src/protocol/intent/intent.ts +12 -0
  97. package/src/protocol/snapshot/index.ts +45 -0
  98. package/src/protocol/snapshot/snapshot-codec.test.ts +138 -0
  99. package/src/protocol/snapshot/snapshot-codec.ts +71 -0
  100. package/src/protocol/snapshot/snapshot-registry.test.ts +302 -0
  101. package/src/protocol/snapshot/snapshot-registry.ts +162 -0
  102. package/src/protocol/snapshot/snapshot.test.ts +76 -0
  103. package/src/protocol/snapshot/snapshot.ts +41 -0
@@ -0,0 +1,158 @@
1
+ /**
2
+ * @description
3
+ * A utility class for managing fixed-rate update ticks, useful for deterministic behaviour
4
+ * in both client and server.
5
+ *
6
+ * The `FixedTicker` accumulates elapsed time and determines how many fixed-interval "ticks"
7
+ * should be processed based on a specified tick rate. It also limits the maximum number of
8
+ * ticks per frame to prevent runaway update loops when frame times are long.
9
+ *
10
+ *
11
+ * @remarks
12
+ * - The `tick` method should be called once per frame, passing the elapsed time in seconds.
13
+ * - The class ensures that no more than a safe number of ticks are processed per frame.
14
+ */
15
+ export class FixedTicker {
16
+ /**
17
+ * @description Accumulator for the time passed since the last tick
18
+ */
19
+ private accumulator = 0;
20
+
21
+ /**
22
+ * @description Rate of ticks per second
23
+ */
24
+ rate: number;
25
+
26
+ /**
27
+ * @description
28
+ * Interval in milliseconds per tick
29
+ */
30
+ private intervalMs: number;
31
+
32
+ /**
33
+ * @description
34
+ * Maximum amount of ticks to run per frame, to avoid
35
+ * running too many ticks in a single frame.
36
+ */
37
+ private maxTicksPerFrame: number;
38
+
39
+ /**
40
+ * @description
41
+ * Callback to execute on each tick
42
+ */
43
+ private onTick: (deltaTime: number, tick?: number) => void;
44
+
45
+ /**
46
+ * @description
47
+ * Optional callback to execute when ticks are skipped due to high delta time.
48
+ * This can be useful for debugging or logging purposes.
49
+ */
50
+ private onTickSkipped?: (skippedTicks: number) => void;
51
+
52
+ /**
53
+ * @description
54
+ * Internal counter for the number of ticks processed.
55
+ */
56
+ private _tickCount = 0;
57
+
58
+ constructor({ rate, onTick }: FixedTickerProps) {
59
+ this.rate = rate;
60
+ this.intervalMs = 1000 / this.rate;
61
+ this.onTick = onTick;
62
+
63
+ // Allow up to rate/2 ticks per frame, but at least 1.
64
+ // Prevents runaway loops if delta is too large.
65
+ this.maxTicksPerFrame = Math.max(1, Math.floor(rate / 2));
66
+ }
67
+
68
+ /**
69
+ * @description
70
+ * Returns how many ticks to run.
71
+ *
72
+ * @param deltaTime Delta time in seconds
73
+ * @returns {number} Amount of ticks to run
74
+ */
75
+ private getTicks(deltaTime: number): number {
76
+ this.accumulator += deltaTime * 1000;
77
+
78
+ let ticks = 0;
79
+
80
+ while (
81
+ this.accumulator >= this.intervalMs &&
82
+ ticks < this.maxTicksPerFrame
83
+ ) {
84
+ this.accumulator -= this.intervalMs;
85
+ ticks++;
86
+ }
87
+
88
+ const skippedTicks = Math.floor(this.accumulator / this.intervalMs);
89
+ if (skippedTicks > 0 && this.onTickSkipped) {
90
+ this.onTickSkipped(skippedTicks);
91
+ }
92
+
93
+ return ticks;
94
+ }
95
+
96
+ /**
97
+ * @description
98
+ * Processes the ticks based on the elapsed time.
99
+ *
100
+ * @param deltaTime Delta time in seconds
101
+ */
102
+ tick(deltaTime: number): void {
103
+ const ticks = this.getTicks(deltaTime);
104
+
105
+ for (let i = 0; i < ticks; i++) {
106
+ this.onTick(1 / this.rate, this._tickCount++);
107
+ }
108
+ }
109
+
110
+ /**
111
+ * @description
112
+ * Returns the number of ticks processed since the last reset.
113
+ *
114
+ * @returns {number} Number of ticks processed
115
+ */
116
+ get tickCount(): number {
117
+ return this._tickCount;
118
+ }
119
+
120
+ /**
121
+ * @description
122
+ * Resets the tick count to zero.
123
+ */
124
+ resetTickCount(): void {
125
+ this._tickCount = 0;
126
+ }
127
+
128
+ /**
129
+ * @description
130
+ * Returns the accumulated time in seconds, useful for interpolation.
131
+ *
132
+ * @returns {number} Accumulated time in seconds
133
+ */
134
+ get accumulatedTime(): number {
135
+ return this.accumulator / 1000; // Convert to seconds
136
+ }
137
+ }
138
+
139
+ interface FixedTickerProps {
140
+ /**
141
+ * @description
142
+ * Rate of ticks per second
143
+ */
144
+ rate: number;
145
+
146
+ /**
147
+ * @description
148
+ * Callback to execute on each tick
149
+ */
150
+ onTick: (deltaTime: number, tick?: number) => void;
151
+
152
+ /**
153
+ * @description
154
+ * Optional callback to execute when ticks are skipped due to high delta time.
155
+ * This can be useful for debugging or logging purposes.
156
+ */
157
+ onTickSkipped?: (skippedTicks: number) => void
158
+ }
@@ -0,0 +1 @@
1
+ export * from './fixed-ticker';
@@ -0,0 +1,18 @@
1
+ # generateId
2
+
3
+ A simple utility function to generate unique 64-bit identifiers using the Web Crypto API.
4
+ The IDs are returned as hexadecimal strings and work in both modern browsers and Node.js (v15+).
5
+
6
+ ## Features
7
+
8
+ - Generates cryptographically strong 64-bit IDs.
9
+ - Returns hexadecimal strings (always padded to 16 characters).
10
+ - Works in browsers and Node ≥15 without any imports.
11
+ - Fast and clean using BigInt arithmetic.
12
+
13
+ ## Usage
14
+
15
+ ```typescript
16
+ import { generateId } from './generate-id';
17
+ const id = generateId(); // akwats 16-character hex string
18
+ ```
@@ -0,0 +1,79 @@
1
+ import { describe, expect, test } from "bun:test";
2
+ import { generateId } from "./generate-id";
3
+
4
+ describe("generateId", () => {
5
+ test("should generate an ID with default length of 16", () => {
6
+ const id = generateId();
7
+ expect(id.length).toBe(16);
8
+ });
9
+
10
+ test("should generate a hexadecimal string", () => {
11
+ const id = generateId();
12
+ expect(id).toMatch(/^[0-9a-f]+$/);
13
+ });
14
+
15
+ test("should generate unique IDs", () => {
16
+ const ids = new Set();
17
+ for (let i = 0; i < 1000; i++) {
18
+ ids.add(generateId());
19
+ }
20
+ expect(ids.size).toBe(1000);
21
+ });
22
+
23
+ test("should include prefix when provided", () => {
24
+ const prefix = "user_";
25
+ const id = generateId({ prefix });
26
+ expect(id.startsWith(prefix)).toBe(true);
27
+ });
28
+
29
+ test("should maintain total size including prefix", () => {
30
+ const prefix = "user_";
31
+ const size = 20;
32
+ const id = generateId({ prefix, size });
33
+ expect(id.length).toBe(size);
34
+ expect(id.startsWith(prefix)).toBe(true);
35
+ });
36
+
37
+ test("should generate custom size without prefix", () => {
38
+ const size = 32;
39
+ const id = generateId({ size });
40
+ expect(id.length).toBe(size);
41
+ });
42
+
43
+ test("should enforce minimum of 8 hex characters", () => {
44
+ const prefix = "verylongprefix_";
45
+ const size = 10; // shorter than prefix + 8
46
+ const id = generateId({ prefix, size });
47
+ // Should be prefix + at least 8 hex chars
48
+ expect(id.length).toBeGreaterThanOrEqual(prefix.length + 8);
49
+ });
50
+
51
+ test("should pad ID to desired length", () => {
52
+ const size = 24;
53
+ const id = generateId({ size });
54
+ expect(id.length).toBe(size);
55
+ });
56
+
57
+ test("should generate different IDs on consecutive calls", () => {
58
+ const id1 = generateId();
59
+ const id2 = generateId();
60
+ expect(id1).not.toBe(id2);
61
+ });
62
+
63
+ test("should work with various prefix lengths", () => {
64
+ const prefixes = ["a", "ab", "abc", "player_", "super_long_prefix_"];
65
+ prefixes.forEach((prefix) => {
66
+ const id = generateId({ prefix });
67
+ expect(id.startsWith(prefix)).toBe(true);
68
+ });
69
+ });
70
+
71
+ test("should generate valid hex with leading zeros preserved", () => {
72
+ // Generate many IDs to ensure padding works correctly
73
+ for (let i = 0; i < 100; i++) {
74
+ const id = generateId({ size: 16 });
75
+ expect(id.length).toBe(16);
76
+ expect(id).toMatch(/^[0-9a-f]{16}$/);
77
+ }
78
+ });
79
+ });
@@ -0,0 +1,37 @@
1
+ interface GenerateIdOptions {
2
+ /** Optional prefix to prepend to the ID */
3
+ prefix?: string;
4
+ /** Total length of the returned ID including prefix (default 16) */
5
+ size?: number;
6
+ }
7
+
8
+ /**
9
+ * @description
10
+ * Generates a unique identifier as a hexadecimal string.
11
+ * Can include a prefix and a custom total length.
12
+ *
13
+ * @param options Optional configuration: prefix and total size
14
+ * @returns A unique identifier string
15
+ *
16
+ * @example
17
+ * generateId(); // "f3a2b1c4d5e67890"
18
+ * generateId({ prefix: 'user_' }); // "user_f3a2b1c4d5e67890"
19
+ * generateId({ prefix: 'user_', size: 24 }); // "user_00f3a2b1c4d5e67890"
20
+ */
21
+ export function generateId(options: GenerateIdOptions = {}): string {
22
+ const { prefix = "", size = 16 } = options;
23
+
24
+ // compute number of hex characters to generate (subtract prefix length)
25
+ const hexLength = Math.max(size - prefix.length, 8); // min 8 hex chars
26
+
27
+ // number of 32-bit integers needed to cover the hexLength
28
+ const numInts = Math.ceil(hexLength / 8);
29
+
30
+ const arr = crypto.getRandomValues(new Uint32Array(numInts));
31
+ let id = arr.reduce((acc, val) => acc + val.toString(16).padStart(8, "0"), "");
32
+
33
+ // truncate/pad to desired length
34
+ id = id.slice(0, hexLength).padStart(hexLength, "0");
35
+
36
+ return `${prefix}${id}`;
37
+ }
@@ -0,0 +1 @@
1
+ export * from './generate-id';
@@ -0,0 +1,8 @@
1
+ export * from './binary-codec';
2
+ export * from './events';
3
+ export * from './fixed-ticker';
4
+ export * from './generate-id';
5
+ export * from './lerp';
6
+ export * from './navmesh';
7
+ export * from './pooled-codec';
8
+ export * from './prediction';
@@ -0,0 +1,79 @@
1
+ # lerp
2
+
3
+ A simple utility function for linear interpolation between two numeric values. Perfect for smooth animations, transitions, and value easing in game development.
4
+
5
+ ## Features
6
+
7
+ - Clean linear interpolation implementation.
8
+ - Unclamped by design - allows extrapolation when t is outside [0, 1].
9
+ - Zero dependencies.
10
+ - Works in browsers and Node.js.
11
+ - TypeScript support with proper type definitions.
12
+
13
+ ## Usage
14
+
15
+ ```typescript
16
+ import { lerp } from './lerp';
17
+
18
+ // Basic interpolation
19
+ const value = lerp(0, 100, 0.5); // Returns 50
20
+
21
+ // Animation example
22
+ const startPos = 0;
23
+ const endPos = 100;
24
+ const progress = 0.75;
25
+ const currentPos = lerp(startPos, endPos, progress); // Returns 75
26
+
27
+ // Smooth camera movement
28
+ function updateCamera(deltaTime: number) {
29
+ const t = deltaTime * smoothingFactor;
30
+ camera.x = lerp(camera.x, target.x, t);
31
+ camera.y = lerp(camera.y, target.y, t);
32
+ }
33
+
34
+ // Color transitions
35
+ const r = lerp(startColor.r, endColor.r, progress);
36
+ const g = lerp(startColor.g, endColor.g, progress);
37
+ const b = lerp(startColor.b, endColor.b, progress);
38
+ ```
39
+
40
+ ## Parameters
41
+
42
+ - `start` (number): The starting value (returned when t = 0)
43
+ - `end` (number): The ending value (returned when t = 1)
44
+ - `t` (number): The interpolation factor, typically in range [0, 1]
45
+
46
+ ## Returns
47
+
48
+ `number` - The interpolated value between start and end.
49
+
50
+ ## Extrapolation
51
+
52
+ The function does not clamp the `t` parameter, allowing extrapolation:
53
+
54
+ ```typescript
55
+ lerp(0, 100, 1.5); // Returns 150 (extrapolated beyond end)
56
+ lerp(0, 100, -0.5); // Returns -50 (extrapolated before start)
57
+ ```
58
+
59
+ If you need clamped interpolation, combine with a clamp function:
60
+
61
+ ```typescript
62
+ function clampedLerp(start: number, end: number, t: number): number {
63
+ const clampedT = Math.max(0, Math.min(1, t));
64
+ return lerp(start, end, clampedT);
65
+ }
66
+ ```
67
+
68
+ ## Common Use Cases
69
+
70
+ - **Smooth animations**: Interpolate position, rotation, scale over time
71
+ - **Camera movement**: Create smooth camera following behavior
72
+ - **UI transitions**: Fade effects, sliding panels, progress bars
73
+ - **Color blending**: Transition between colors smoothly
74
+ - **Value easing**: Gradually approach target values
75
+ - **Physics simulations**: Interpolate between physics states for rendering
76
+
77
+ ---
78
+
79
+ `lerp` provides a foundational building block for smooth, continuous value transitions in game development and interactive applications.
@@ -0,0 +1 @@
1
+ export * from './lerp';
@@ -0,0 +1,90 @@
1
+ import { describe, expect, test } from "bun:test";
2
+ import { lerp } from "./lerp";
3
+
4
+ describe("lerp", () => {
5
+ test("should return start value when t is 0", () => {
6
+ expect(lerp(0, 100, 0)).toBe(0);
7
+ expect(lerp(50, 200, 0)).toBe(50);
8
+ expect(lerp(-10, 10, 0)).toBe(-10);
9
+ });
10
+
11
+ test("should return end value when t is 1", () => {
12
+ expect(lerp(0, 100, 1)).toBe(100);
13
+ expect(lerp(50, 200, 1)).toBe(200);
14
+ expect(lerp(-10, 10, 1)).toBe(10);
15
+ });
16
+
17
+ test("should return midpoint when t is 0.5", () => {
18
+ expect(lerp(0, 100, 0.5)).toBe(50);
19
+ expect(lerp(50, 150, 0.5)).toBe(100);
20
+ expect(lerp(-10, 10, 0.5)).toBe(0);
21
+ });
22
+
23
+ test("should interpolate between positive values", () => {
24
+ expect(lerp(0, 100, 0.25)).toBe(25);
25
+ expect(lerp(0, 100, 0.75)).toBe(75);
26
+ expect(lerp(10, 20, 0.3)).toBeCloseTo(13, 5);
27
+ });
28
+
29
+ test("should interpolate between negative values", () => {
30
+ expect(lerp(-100, -50, 0.5)).toBe(-75);
31
+ expect(lerp(-10, -5, 0.2)).toBe(-9);
32
+ });
33
+
34
+ test("should interpolate from negative to positive", () => {
35
+ expect(lerp(-50, 50, 0.5)).toBe(0);
36
+ expect(lerp(-10, 10, 0.25)).toBe(-5);
37
+ expect(lerp(-10, 10, 0.75)).toBe(5);
38
+ });
39
+
40
+ test("should handle extrapolation when t > 1", () => {
41
+ expect(lerp(0, 100, 1.5)).toBe(150);
42
+ expect(lerp(0, 100, 2)).toBe(200);
43
+ expect(lerp(10, 20, 1.1)).toBeCloseTo(21, 5);
44
+ });
45
+
46
+ test("should handle extrapolation when t < 0", () => {
47
+ expect(lerp(0, 100, -0.5)).toBe(-50);
48
+ expect(lerp(0, 100, -1)).toBe(-100);
49
+ expect(lerp(10, 20, -0.1)).toBeCloseTo(9, 5);
50
+ });
51
+
52
+ test("should handle same start and end values", () => {
53
+ expect(lerp(50, 50, 0)).toBe(50);
54
+ expect(lerp(50, 50, 0.5)).toBe(50);
55
+ expect(lerp(50, 50, 1)).toBe(50);
56
+ expect(lerp(50, 50, 2)).toBe(50);
57
+ });
58
+
59
+ test("should handle floating point values", () => {
60
+ expect(lerp(0.1, 0.9, 0.5)).toBeCloseTo(0.5, 10);
61
+ expect(lerp(1.5, 2.5, 0.3)).toBeCloseTo(1.8, 10);
62
+ });
63
+
64
+ test("should be commutative with inverted t", () => {
65
+ const start = 10;
66
+ const end = 20;
67
+ expect(lerp(start, end, 0.3)).toBeCloseTo(lerp(end, start, 0.7), 10);
68
+ });
69
+
70
+ test("should handle very small differences", () => {
71
+ expect(lerp(1.0, 1.0001, 0.5)).toBeCloseTo(1.00005, 10);
72
+ });
73
+
74
+ test("should handle very large values", () => {
75
+ expect(lerp(1e10, 2e10, 0.5)).toBe(1.5e10);
76
+ });
77
+
78
+ test("should be linear (no easing)", () => {
79
+ const start = 0;
80
+ const end = 100;
81
+ const step = 0.1;
82
+ const expectedDiff = 10;
83
+
84
+ for (let t = 0; t < 1; t += step) {
85
+ const val1 = lerp(start, end, t);
86
+ const val2 = lerp(start, end, t + step);
87
+ expect(val2 - val1).toBeCloseTo(expectedDiff, 5);
88
+ }
89
+ });
90
+ });
@@ -0,0 +1,42 @@
1
+ /**
2
+ * @description
3
+ * Performs linear interpolation between two values.
4
+ *
5
+ * Linear interpolation (lerp) calculates a value between a start and end point
6
+ * based on a normalized interpolation factor (t). When t = 0, the result equals
7
+ * the start value; when t = 1, the result equals the end value. Values of t
8
+ * between 0 and 1 produce intermediate results.
9
+ *
10
+ * @remarks
11
+ * - This function does not clamp the interpolation factor. Values of t outside
12
+ * the range [0, 1] will extrapolate beyond the start and end values.
13
+ * - For clamped interpolation, combine with a clamp function on the t parameter.
14
+ * - Commonly used for smooth animations, camera movements, and value transitions.
15
+ *
16
+ * @param start - The starting value (when t = 0)
17
+ * @param end - The ending value (when t = 1)
18
+ * @param t - The interpolation factor, typically in the range [0, 1]
19
+ *
20
+ * @returns {number} The interpolated value between start and end
21
+ *
22
+ * @example
23
+ * ```typescript
24
+ * // Basic interpolation
25
+ * lerp(0, 100, 0.5); // Returns 50
26
+ * lerp(0, 100, 0); // Returns 0
27
+ * lerp(0, 100, 1); // Returns 100
28
+ *
29
+ * // Animation example
30
+ * const startPos = 0;
31
+ * const endPos = 100;
32
+ * const progress = 0.75;
33
+ * const currentPos = lerp(startPos, endPos, progress); // Returns 75
34
+ *
35
+ * // Extrapolation (t outside [0, 1])
36
+ * lerp(0, 100, 1.5); // Returns 150
37
+ * lerp(0, 100, -0.5); // Returns -50
38
+ * ```
39
+ */
40
+ export function lerp(start: number, end: number, t: number): number {
41
+ return start + (end - start) * t;
42
+ }
@@ -0,0 +1,124 @@
1
+ # NavMesh / Pathfinding Utility
2
+
3
+ A lightweight navigation system for grid-based and hybrid games.
4
+
5
+ Supports:
6
+
7
+ * **Grid A*** pathfinding
8
+ * **Line-of-sight graph navigation**
9
+ * **Dynamic obstacles**
10
+ * **Spatial hashing for fast queries**
11
+ * **Circle / Rect / Polygon obstacles**
12
+ * **Zero rebuilds unless data changes**
13
+
14
+ Designed for **games**, not CAD-grade geometry.
15
+
16
+ ---
17
+
18
+ ## Features
19
+
20
+ * ⚡ **Fast obstacle queries** via spatial hash
21
+ * 🧠 **Smart rebuilds** (version-based, no unnecessary work)
22
+ * 🧩 **Multiple obstacle types**
23
+ * 🧭 **A*** with binary heap
24
+ * 🧱 **Grid or graph navigation**
25
+ * 🔁 Dynamic obstacle add / move / remove
26
+ * 🧪 Deterministic & allocation-safe
27
+
28
+ ---
29
+
30
+ ## Usage
31
+
32
+ ### Create navmesh
33
+
34
+ ```ts
35
+ const nav = new NavMesh('grid'); // or 'graph'
36
+ ```
37
+
38
+ ### Add obstacles
39
+
40
+ ```ts
41
+ nav.addObstacle({
42
+ type: 'circle',
43
+ pos: { x: 5, y: 5 },
44
+ radius: 2
45
+ });
46
+ ```
47
+
48
+ ```ts
49
+ nav.addObstacle({
50
+ type: 'rect',
51
+ pos: { x: 2, y: 3 },
52
+ size: { x: 4, y: 2 },
53
+ });
54
+ ```
55
+
56
+ ```ts
57
+ nav.addObstacle({
58
+ type: 'polygon',
59
+ pos: { x: 10, y: 5 },
60
+ points: [
61
+ { x: 0, y: 0 },
62
+ { x: 2, y: 0 },
63
+ { x: 1, y: 2 },
64
+ ],
65
+ });
66
+ ```
67
+
68
+ ### Move / remove
69
+
70
+ ```ts
71
+ nav.moveObstacle(id, { x: 8, y: 4 });
72
+ nav.removeObstacle(id);
73
+ ```
74
+
75
+ ### Find path
76
+
77
+ ```ts
78
+ const path = nav.findPath({
79
+ from: { x: 1, y: 1 },
80
+ to: { x: 10, y: 8 }
81
+ });
82
+ ```
83
+
84
+ ---
85
+
86
+ ## Navigation Modes
87
+
88
+ ### `grid`
89
+
90
+ * A* over grid cells
91
+ * Accurate
92
+ * Best for RTS / tactics / tile games
93
+
94
+ ### `graph`
95
+
96
+ * Line-of-sight check
97
+ * Falls back to grid if blocked
98
+ * Faster for open maps
99
+
100
+ ---
101
+
102
+ ## Performance
103
+
104
+ | Feature | Cost |
105
+ | -------------- | --------------------------------- |
106
+ | Obstacle query | **O(1)** avg |
107
+ | Grid rebuild | O(n × area) |
108
+ | Pathfinding | O(bᵈ log n) |
109
+ | Memory | Minimal, no allocations per frame |
110
+
111
+ Handles:
112
+
113
+ * 1k+ obstacles
114
+ * 10k+ A* nodes
115
+ * Real-time updates
116
+
117
+ ---
118
+
119
+ ## Notes
120
+
121
+ * Polygon points **must be local (0,0-based)**
122
+ * Rotation is supported for rects & polygons
123
+ * All math is deterministic
124
+ * No dependencies
@@ -0,0 +1 @@
1
+ export * from './navmesh';