murow 0.0.60 → 0.0.71
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/README.md +52 -37
- package/dist/cjs/core/binary-codec/binary-codec.js +1 -0
- package/dist/cjs/core/binary-codec/index.js +1 -0
- package/dist/cjs/core/driver/driver.js +1 -0
- package/dist/cjs/core/driver/drivers/immediate.js +1 -0
- package/dist/cjs/core/driver/drivers/index.js +1 -0
- package/dist/cjs/core/driver/drivers/raf.js +1 -0
- package/dist/cjs/core/driver/drivers/timeout.js +1 -0
- package/dist/cjs/core/driver/index.js +1 -0
- package/dist/cjs/core/events/event-system.js +1 -0
- package/dist/cjs/core/events/index.js +1 -0
- package/dist/cjs/core/fixed-ticker/fixed-ticker.js +1 -0
- package/dist/cjs/core/fixed-ticker/index.js +1 -0
- package/dist/cjs/core/free-list/free-list.js +1 -0
- package/dist/cjs/core/free-list/index.js +1 -0
- package/dist/cjs/core/generate-id/generate-id.js +1 -0
- package/dist/cjs/core/generate-id/index.js +1 -0
- package/dist/cjs/core/index.js +1 -0
- package/dist/cjs/core/input/index.js +1 -0
- package/dist/cjs/core/input/manager.js +1 -0
- package/dist/cjs/core/input/sources/browser.js +1 -0
- package/dist/cjs/core/input/sources/index.js +1 -0
- package/dist/cjs/core/input/types.js +1 -0
- package/dist/cjs/core/lerp/index.js +1 -0
- package/dist/cjs/core/lerp/lerp.js +1 -0
- package/dist/cjs/core/navmesh/index.js +1 -0
- package/dist/cjs/core/navmesh/navmesh-worker-pool.js +1 -0
- package/dist/cjs/core/navmesh/navmesh.js +1 -0
- package/dist/cjs/core/navmesh/navmesh.worker.js +1 -0
- package/dist/cjs/core/pooled-codec/index.js +1 -0
- package/dist/cjs/core/pooled-codec/pooled-codec.js +1 -0
- package/dist/cjs/core/prediction/index.js +1 -0
- package/dist/cjs/core/prediction/prediction.js +1 -0
- package/dist/cjs/core/ray/index.js +1 -0
- package/dist/cjs/core/ray/ray-2d.js +1 -0
- package/dist/cjs/core/ray/ray-3d.js +1 -0
- package/dist/cjs/core/simple-rng/index.js +1 -0
- package/dist/cjs/core/simple-rng/simple-rng.js +1 -0
- package/dist/cjs/core/sparse-batcher/index.js +1 -0
- package/dist/cjs/core/sparse-batcher/sparse-batcher.js +1 -0
- package/dist/cjs/ecs/component-store.js +1 -0
- package/dist/cjs/ecs/component.js +1 -0
- package/dist/cjs/ecs/entity-handle.js +1 -0
- package/dist/cjs/ecs/index.js +1 -0
- package/dist/cjs/ecs/system-builder.js +1 -0
- package/dist/cjs/ecs/world-systems.js +1 -0
- package/dist/cjs/ecs/world.js +1 -0
- package/dist/cjs/game/index.js +1 -0
- package/dist/cjs/game/loop/index.js +1 -0
- package/dist/cjs/game/loop/loop.js +1 -0
- package/dist/cjs/index.js +1 -0
- package/dist/cjs/net/adapters/browser-websocket.js +1 -0
- package/dist/cjs/net/adapters/bun-websocket.js +1 -0
- package/dist/cjs/net/buffer-pool.js +1 -0
- package/dist/cjs/net/client.js +1 -0
- package/dist/cjs/net/index.js +1 -0
- package/dist/cjs/net/server.js +1 -0
- package/dist/cjs/net/types.js +1 -0
- package/dist/cjs/net/validators.js +1 -0
- package/dist/cjs/protocol/index.js +1 -0
- package/dist/cjs/protocol/intent/define-intent.js +1 -0
- package/dist/cjs/protocol/intent/index.js +1 -0
- package/dist/cjs/protocol/intent/intent-registry.js +1 -0
- package/dist/cjs/protocol/intent/intent.js +1 -0
- package/dist/cjs/protocol/rpc/define-rpc.js +1 -0
- package/dist/cjs/protocol/rpc/index.js +1 -0
- package/dist/cjs/protocol/rpc/rpc-registry.js +1 -0
- package/dist/cjs/protocol/rpc/rpc.js +1 -0
- package/dist/cjs/protocol/snapshot/index.js +1 -0
- package/dist/cjs/protocol/snapshot/snapshot-codec.js +1 -0
- package/dist/cjs/protocol/snapshot/snapshot-registry.js +1 -0
- package/dist/cjs/protocol/snapshot/snapshot.js +1 -0
- package/dist/cjs/renderer/base-2d-renderer.js +1 -0
- package/dist/cjs/renderer/base-3d-renderer.js +1 -0
- package/dist/cjs/renderer/base-renderer.js +1 -0
- package/dist/cjs/renderer/index.js +1 -0
- package/dist/cjs/renderer/types.js +1 -0
- package/dist/esm/core/binary-codec/binary-codec.js +1 -0
- package/dist/esm/core/binary-codec/index.js +1 -0
- package/dist/esm/core/driver/driver.js +1 -0
- package/dist/esm/core/driver/drivers/immediate.js +1 -0
- package/dist/esm/core/driver/drivers/index.js +1 -0
- package/dist/esm/core/driver/drivers/raf.js +1 -0
- package/dist/esm/core/driver/drivers/timeout.js +1 -0
- package/dist/esm/core/driver/index.js +1 -0
- package/dist/esm/core/events/event-system.js +1 -0
- package/dist/esm/core/events/index.js +1 -0
- package/dist/esm/core/fixed-ticker/fixed-ticker.js +1 -0
- package/dist/esm/core/fixed-ticker/index.js +1 -0
- package/dist/esm/core/free-list/free-list.js +1 -0
- package/dist/esm/core/free-list/index.js +1 -0
- package/dist/esm/core/generate-id/generate-id.js +1 -0
- package/dist/esm/core/generate-id/index.js +1 -0
- package/dist/esm/core/index.js +1 -0
- package/dist/esm/core/input/index.js +1 -0
- package/dist/esm/core/input/manager.js +1 -0
- package/dist/esm/core/input/sources/browser.js +1 -0
- package/dist/esm/core/input/sources/index.js +1 -0
- package/dist/esm/core/input/types.js +0 -0
- package/dist/esm/core/lerp/index.js +1 -0
- package/dist/esm/core/lerp/lerp.js +1 -0
- package/dist/esm/core/navmesh/index.js +1 -0
- package/dist/esm/core/navmesh/navmesh-worker-pool.js +1 -0
- package/dist/esm/core/navmesh/navmesh.js +1 -0
- package/dist/esm/core/navmesh/navmesh.worker.js +1 -0
- package/dist/esm/core/pooled-codec/index.js +1 -0
- package/dist/esm/core/pooled-codec/pooled-codec.js +1 -0
- package/dist/esm/core/prediction/index.js +1 -0
- package/dist/esm/core/prediction/prediction.js +1 -0
- package/dist/esm/core/ray/index.js +1 -0
- package/dist/esm/core/ray/ray-2d.js +1 -0
- package/dist/esm/core/ray/ray-3d.js +1 -0
- package/dist/esm/core/simple-rng/index.js +1 -0
- package/dist/esm/core/simple-rng/simple-rng.js +1 -0
- package/dist/esm/core/sparse-batcher/index.js +1 -0
- package/dist/esm/core/sparse-batcher/sparse-batcher.js +1 -0
- package/dist/esm/ecs/component-store.js +1 -0
- package/dist/esm/ecs/component.js +1 -0
- package/dist/esm/ecs/entity-handle.js +1 -0
- package/dist/esm/ecs/index.js +1 -0
- package/dist/esm/ecs/system-builder.js +1 -0
- package/dist/esm/ecs/world-systems.js +1 -0
- package/dist/esm/ecs/world.js +1 -0
- package/dist/esm/game/index.js +1 -0
- package/dist/esm/game/loop/index.js +1 -0
- package/dist/esm/game/loop/loop.js +1 -0
- package/dist/esm/index.js +1 -0
- package/dist/esm/net/adapters/browser-websocket.js +1 -0
- package/dist/esm/net/adapters/bun-websocket.js +1 -0
- package/dist/esm/net/buffer-pool.js +1 -0
- package/dist/esm/net/client.js +1 -0
- package/dist/esm/net/index.js +1 -0
- package/dist/esm/net/server.js +1 -0
- package/dist/esm/net/types.js +1 -0
- package/dist/esm/net/validators.js +1 -0
- package/dist/esm/protocol/index.js +1 -0
- package/dist/esm/protocol/intent/define-intent.js +1 -0
- package/dist/esm/protocol/intent/index.js +1 -0
- package/dist/esm/protocol/intent/intent-registry.js +1 -0
- package/dist/esm/protocol/intent/intent.js +0 -0
- package/dist/esm/protocol/rpc/define-rpc.js +1 -0
- package/dist/esm/protocol/rpc/index.js +1 -0
- package/dist/esm/protocol/rpc/rpc-registry.js +1 -0
- package/dist/esm/protocol/rpc/rpc.js +0 -0
- package/dist/esm/protocol/snapshot/index.js +1 -0
- package/dist/esm/protocol/snapshot/snapshot-codec.js +1 -0
- package/dist/esm/protocol/snapshot/snapshot-registry.js +1 -0
- package/dist/esm/protocol/snapshot/snapshot.js +1 -0
- package/dist/esm/renderer/base-2d-renderer.js +1 -0
- package/dist/esm/renderer/base-3d-renderer.js +1 -0
- package/dist/esm/renderer/base-renderer.js +1 -0
- package/dist/esm/renderer/index.js +1 -0
- package/dist/esm/renderer/types.js +0 -0
- package/dist/{core → types/core}/binary-codec/binary-codec.d.ts +4 -0
- package/dist/{core/loop → types/core/driver}/drivers/immediate.d.ts +1 -1
- package/dist/{core/loop → types/core/driver}/drivers/raf.d.ts +1 -1
- package/dist/{core/loop → types/core/driver}/drivers/timeout.d.ts +1 -1
- package/dist/{core/loop → types/core/driver}/index.d.ts +1 -1
- package/dist/{core → types/core}/events/event-system.d.ts +14 -33
- package/dist/{core → types/core}/fixed-ticker/fixed-ticker.d.ts +1 -1
- package/dist/types/core/free-list/free-list.d.ts +31 -0
- package/dist/types/core/free-list/index.d.ts +1 -0
- package/dist/{core → types/core}/index.d.ts +7 -1
- package/dist/types/core/input/index.d.ts +3 -0
- package/dist/types/core/input/manager.d.ts +56 -0
- package/dist/types/core/input/sources/browser.d.ts +9 -0
- package/dist/types/core/input/sources/index.d.ts +1 -0
- package/dist/types/core/input/types.d.ts +36 -0
- package/dist/{core → types/core}/navmesh/navmesh.d.ts +1 -21
- package/dist/types/core/ray/index.d.ts +2 -0
- package/dist/types/core/ray/ray-2d.d.ts +37 -0
- package/dist/types/core/ray/ray-3d.d.ts +42 -0
- package/dist/types/core/simple-rng/index.d.ts +1 -0
- package/dist/types/core/simple-rng/simple-rng.d.ts +36 -0
- package/dist/types/core/sparse-batcher/index.d.ts +1 -0
- package/dist/types/core/sparse-batcher/sparse-batcher.d.ts +55 -0
- package/dist/{ecs → types/ecs}/system-builder.d.ts +20 -9
- package/dist/{ecs → types/ecs}/world.d.ts +11 -0
- package/dist/types/game/index.d.ts +1 -0
- package/dist/types/game/loop/index.d.ts +1 -0
- package/dist/types/game/loop/loop.d.ts +175 -0
- package/dist/{index.d.ts → types/index.d.ts} +2 -0
- package/dist/{net → types/net}/index.d.ts +2 -2
- package/dist/{net → types/net}/server.d.ts +39 -19
- package/dist/{protocol → types/protocol}/intent/define-intent.d.ts +15 -0
- package/dist/{protocol → types/protocol}/intent/index.d.ts +1 -1
- package/dist/types/renderer/base-2d-renderer.d.ts +13 -0
- package/dist/types/renderer/base-3d-renderer.d.ts +10 -0
- package/dist/types/renderer/base-renderer.d.ts +21 -0
- package/dist/types/renderer/index.d.ts +4 -0
- package/dist/types/renderer/types.d.ts +79 -0
- package/dist/webgpu/cjs/index.js +6004 -0
- package/dist/webgpu/esm/index.js +5972 -0
- package/dist/webgpu/types/2d/animation.d.ts +97 -0
- package/dist/webgpu/types/2d/renderer.d.ts +55 -0
- package/dist/webgpu/types/2d/shader.d.ts +61 -0
- package/dist/webgpu/types/2d/sprite-accessor.d.ts +47 -0
- package/dist/webgpu/types/2d/sprite-accessor.test.d.ts +1 -0
- package/dist/webgpu/types/3d/gltf-skin-parser.d.ts +101 -0
- package/dist/webgpu/types/3d/morph-animation.d.ts +69 -0
- package/dist/webgpu/types/3d/morph-animation.test.d.ts +1 -0
- package/dist/webgpu/types/3d/renderer.d.ts +216 -0
- package/dist/webgpu/types/3d/shader.d.ts +136 -0
- package/dist/webgpu/types/3d/skeletal-animation-compute/index.d.ts +2 -0
- package/dist/webgpu/types/3d/skeletal-animation-compute/kernel.d.ts +8 -0
- package/dist/webgpu/types/3d/skeletal-animation-compute/packer.d.ts +32 -0
- package/dist/webgpu/types/3d/skeletal-animation.d.ts +90 -0
- package/dist/webgpu/types/camera/camera-2d.d.ts +53 -0
- package/dist/webgpu/types/camera/camera-2d.test.d.ts +1 -0
- package/dist/webgpu/types/camera/camera-3d.d.ts +81 -0
- package/dist/webgpu/types/camera/camera-3d.test.d.ts +1 -0
- package/dist/webgpu/types/camera/index.d.ts +2 -0
- package/dist/webgpu/types/compute/compute-builder.d.ts +123 -0
- package/dist/webgpu/types/compute/compute-builder.test.d.ts +1 -0
- package/dist/webgpu/types/core/constants.d.ts +59 -0
- package/dist/webgpu/types/core/constants.test.d.ts +1 -0
- package/dist/webgpu/types/core/index.d.ts +2 -0
- package/dist/webgpu/types/core/math.d.ts +37 -0
- package/dist/webgpu/types/core/types.d.ts +125 -0
- package/dist/webgpu/types/core/types.test.d.ts +1 -0
- package/dist/webgpu/types/geometry/built-in.d.ts +58 -0
- package/dist/webgpu/types/geometry/built-in.test.d.ts +1 -0
- package/dist/webgpu/types/geometry/geometry-builder.d.ts +281 -0
- package/dist/webgpu/types/geometry/geometry-builder.test.d.ts +1 -0
- package/dist/webgpu/types/geometry/index.d.ts +2 -0
- package/dist/webgpu/types/index.d.ts +32 -0
- package/dist/webgpu/types/particle/emitter.d.ts +36 -0
- package/dist/webgpu/types/shaders/index.d.ts +2 -0
- package/dist/webgpu/types/shaders/runtime-transpile.d.ts +18 -0
- package/dist/webgpu/types/shaders/sprite-2d.wgsl.d.ts +10 -0
- package/dist/webgpu/types/shaders/typegpu.d.ts +9 -0
- package/dist/webgpu/types/shaders/utils.d.ts +28 -0
- package/dist/webgpu/types/shaders/utils.test.d.ts +1 -0
- package/dist/webgpu/types/spritesheet/index.d.ts +1 -0
- package/dist/webgpu/types/spritesheet/spritesheet.d.ts +57 -0
- package/dist/webgpu/types/spritesheet/spritesheet.test.d.ts +1 -0
- package/package.json +96 -26
- package/dist/core/binary-codec/binary-codec.js +0 -354
- package/dist/core/binary-codec/index.js +0 -1
- package/dist/core/events/event-system.js +0 -88
- package/dist/core/events/index.js +0 -1
- package/dist/core/fixed-ticker/fixed-ticker.js +0 -101
- package/dist/core/fixed-ticker/index.js +0 -1
- package/dist/core/generate-id/generate-id.js +0 -25
- package/dist/core/generate-id/index.js +0 -1
- package/dist/core/index.js +0 -9
- package/dist/core/lerp/index.js +0 -1
- package/dist/core/lerp/lerp.js +0 -42
- package/dist/core/loop/drivers/immediate.js +0 -61
- package/dist/core/loop/drivers/index.js +0 -3
- package/dist/core/loop/drivers/raf.js +0 -62
- package/dist/core/loop/drivers/timeout.js +0 -71
- package/dist/core/loop/index.js +0 -2
- package/dist/core/loop/loop.js +0 -47
- package/dist/core/navmesh/index.js +0 -1
- package/dist/core/navmesh/navmesh-worker-pool.js +0 -180
- package/dist/core/navmesh/navmesh.js +0 -799
- package/dist/core/navmesh/navmesh.worker.js +0 -79
- package/dist/core/pooled-codec/index.js +0 -1
- package/dist/core/pooled-codec/pooled-codec.js +0 -410
- package/dist/core/prediction/index.js +0 -1
- package/dist/core/prediction/prediction.js +0 -99
- package/dist/core.esm.js +0 -1
- package/dist/core.js +0 -1
- package/dist/ecs/component-store.js +0 -175
- package/dist/ecs/component.js +0 -43
- package/dist/ecs/entity-handle.js +0 -515
- package/dist/ecs/example.js +0 -125
- package/dist/ecs/index.js +0 -4
- package/dist/ecs/system-builder.js +0 -180
- package/dist/ecs/system.d.ts +0 -63
- package/dist/ecs/system.js +0 -92
- package/dist/ecs/world-systems.js +0 -79
- package/dist/ecs/world.js +0 -684
- package/dist/index.js +0 -24
- package/dist/net/adapters/browser-websocket.js +0 -74
- package/dist/net/adapters/bun-websocket.js +0 -245
- package/dist/net/buffer-pool.js +0 -89
- package/dist/net/client.js +0 -586
- package/dist/net/index.js +0 -58
- package/dist/net/server.js +0 -938
- package/dist/net/types.js +0 -31
- package/dist/net/validators.js +0 -88
- package/dist/protocol/index.js +0 -92
- package/dist/protocol/intent/define-intent.js +0 -125
- package/dist/protocol/intent/index.js +0 -91
- package/dist/protocol/intent/intent-registry.js +0 -91
- package/dist/protocol/rpc/define-rpc.js +0 -84
- package/dist/protocol/rpc/index.js +0 -3
- package/dist/protocol/rpc/rpc-registry.js +0 -159
- package/dist/protocol/rpc/rpc.js +0 -12
- package/dist/protocol/snapshot/index.js +0 -43
- package/dist/protocol/snapshot/snapshot-codec.js +0 -67
- package/dist/protocol/snapshot/snapshot-registry.js +0 -168
- package/dist/protocol/snapshot/snapshot.js +0 -30
- package/src/core/binary-codec/README.md +0 -60
- package/src/core/binary-codec/binary-codec.test.ts +0 -300
- package/src/core/binary-codec/binary-codec.ts +0 -448
- package/src/core/binary-codec/index.ts +0 -1
- package/src/core/events/README.md +0 -47
- package/src/core/events/event-system.test.ts +0 -243
- package/src/core/events/event-system.ts +0 -140
- package/src/core/events/index.ts +0 -1
- package/src/core/fixed-ticker/README.md +0 -77
- package/src/core/fixed-ticker/fixed-ticker.test.ts +0 -151
- package/src/core/fixed-ticker/fixed-ticker.ts +0 -169
- package/src/core/fixed-ticker/index.ts +0 -1
- package/src/core/generate-id/README.md +0 -18
- package/src/core/generate-id/generate-id.test.ts +0 -79
- package/src/core/generate-id/generate-id.ts +0 -37
- package/src/core/generate-id/index.ts +0 -1
- package/src/core/index.ts +0 -9
- package/src/core/lerp/README.md +0 -79
- package/src/core/lerp/index.ts +0 -1
- package/src/core/lerp/lerp.test.ts +0 -90
- package/src/core/lerp/lerp.ts +0 -42
- package/src/core/loop/README.md +0 -97
- package/src/core/loop/drivers/immediate.ts +0 -66
- package/src/core/loop/drivers/index.ts +0 -3
- package/src/core/loop/drivers/raf.ts +0 -67
- package/src/core/loop/drivers/timeout.ts +0 -77
- package/src/core/loop/index.ts +0 -2
- package/src/core/loop/loop.test.ts +0 -414
- package/src/core/loop/loop.ts +0 -71
- package/src/core/navmesh/README.md +0 -164
- package/src/core/navmesh/index.ts +0 -1
- package/src/core/navmesh/navmesh-worker-pool.ts +0 -236
- package/src/core/navmesh/navmesh-workers.test.ts +0 -356
- package/src/core/navmesh/navmesh.test.ts +0 -344
- package/src/core/navmesh/navmesh.ts +0 -1047
- package/src/core/navmesh/navmesh.worker.ts +0 -147
- package/src/core/pooled-codec/README.md +0 -70
- package/src/core/pooled-codec/index.ts +0 -1
- package/src/core/pooled-codec/pooled-codec.test.ts +0 -862
- package/src/core/pooled-codec/pooled-codec.ts +0 -504
- package/src/core/prediction/README.md +0 -64
- package/src/core/prediction/index.ts +0 -1
- package/src/core/prediction/prediction.test.ts +0 -423
- package/src/core/prediction/prediction.ts +0 -112
- package/src/ecs/README.md +0 -427
- package/src/ecs/benchmark.test.ts +0 -1645
- package/src/ecs/component-store.ts +0 -198
- package/src/ecs/component.ts +0 -90
- package/src/ecs/entity-handle.test.ts +0 -393
- package/src/ecs/entity-handle.ts +0 -563
- package/src/ecs/example.ts +0 -152
- package/src/ecs/index.ts +0 -4
- package/src/ecs/system-builder.ts +0 -309
- package/src/ecs/system.ts +0 -111
- package/src/ecs/world-systems.ts +0 -83
- package/src/ecs/world.test.ts +0 -310
- package/src/ecs/world.ts +0 -828
- package/src/index.ts +0 -28
- package/src/net/README.md +0 -474
- package/src/net/adapters/browser-websocket.ts +0 -86
- package/src/net/adapters/bun-websocket.ts +0 -292
- package/src/net/buffer-pool.ts +0 -106
- package/src/net/client.test.ts +0 -807
- package/src/net/client.ts +0 -695
- package/src/net/index.ts +0 -60
- package/src/net/server.test.ts +0 -799
- package/src/net/server.ts +0 -1116
- package/src/net/types.ts +0 -228
- package/src/net/validators.ts +0 -104
- package/src/protocol/README.md +0 -469
- package/src/protocol/index.ts +0 -93
- package/src/protocol/intent/define-intent.test.ts +0 -397
- package/src/protocol/intent/define-intent.ts +0 -182
- package/src/protocol/intent/index.ts +0 -94
- package/src/protocol/intent/intent-registry.test.ts +0 -198
- package/src/protocol/intent/intent-registry.ts +0 -112
- package/src/protocol/intent/intent.ts +0 -12
- package/src/protocol/rpc/define-rpc.test.ts +0 -141
- package/src/protocol/rpc/define-rpc.ts +0 -113
- package/src/protocol/rpc/index.ts +0 -3
- package/src/protocol/rpc/rpc-registry.test.ts +0 -168
- package/src/protocol/rpc/rpc-registry.ts +0 -176
- package/src/protocol/rpc/rpc.ts +0 -37
- package/src/protocol/snapshot/index.ts +0 -45
- package/src/protocol/snapshot/snapshot-codec.test.ts +0 -138
- package/src/protocol/snapshot/snapshot-codec.ts +0 -87
- package/src/protocol/snapshot/snapshot-registry.test.ts +0 -310
- package/src/protocol/snapshot/snapshot-registry.ts +0 -201
- package/src/protocol/snapshot/snapshot.test.ts +0 -76
- package/src/protocol/snapshot/snapshot.ts +0 -41
- /package/dist/{core → types/core}/binary-codec/index.d.ts +0 -0
- /package/dist/{core/loop/loop.d.ts → types/core/driver/driver.d.ts} +0 -0
- /package/dist/{core/loop → types/core/driver}/drivers/index.d.ts +0 -0
- /package/dist/{core → types/core}/events/index.d.ts +0 -0
- /package/dist/{core → types/core}/fixed-ticker/index.d.ts +0 -0
- /package/dist/{core → types/core}/generate-id/generate-id.d.ts +0 -0
- /package/dist/{core → types/core}/generate-id/index.d.ts +0 -0
- /package/dist/{core → types/core}/lerp/index.d.ts +0 -0
- /package/dist/{core → types/core}/lerp/lerp.d.ts +0 -0
- /package/dist/{core → types/core}/navmesh/index.d.ts +0 -0
- /package/dist/{core → types/core}/navmesh/navmesh-worker-pool.d.ts +0 -0
- /package/dist/{core → types/core}/navmesh/navmesh.worker.d.ts +0 -0
- /package/dist/{core → types/core}/pooled-codec/index.d.ts +0 -0
- /package/dist/{core → types/core}/pooled-codec/pooled-codec.d.ts +0 -0
- /package/dist/{core → types/core}/prediction/index.d.ts +0 -0
- /package/dist/{core → types/core}/prediction/prediction.d.ts +0 -0
- /package/dist/{ecs → types/ecs}/component-store.d.ts +0 -0
- /package/dist/{ecs → types/ecs}/component.d.ts +0 -0
- /package/dist/{ecs → types/ecs}/entity-handle.d.ts +0 -0
- /package/dist/{ecs → types/ecs}/example.d.ts +0 -0
- /package/dist/{ecs → types/ecs}/index.d.ts +0 -0
- /package/dist/{ecs → types/ecs}/world-systems.d.ts +0 -0
- /package/dist/{net → types/net}/adapters/browser-websocket.d.ts +0 -0
- /package/dist/{net → types/net}/adapters/bun-websocket.d.ts +0 -0
- /package/dist/{net → types/net}/buffer-pool.d.ts +0 -0
- /package/dist/{net → types/net}/client.d.ts +0 -0
- /package/dist/{net → types/net}/types.d.ts +0 -0
- /package/dist/{net → types/net}/validators.d.ts +0 -0
- /package/dist/{protocol → types/protocol}/index.d.ts +0 -0
- /package/dist/{protocol → types/protocol}/intent/intent-registry.d.ts +0 -0
- /package/dist/{protocol → types/protocol}/intent/intent.d.ts +0 -0
- /package/dist/{protocol → types/protocol}/rpc/define-rpc.d.ts +0 -0
- /package/dist/{protocol → types/protocol}/rpc/index.d.ts +0 -0
- /package/dist/{protocol → types/protocol}/rpc/rpc-registry.d.ts +0 -0
- /package/dist/{protocol → types/protocol}/rpc/rpc.d.ts +0 -0
- /package/dist/{protocol → types/protocol}/snapshot/index.d.ts +0 -0
- /package/dist/{protocol → types/protocol}/snapshot/snapshot-codec.d.ts +0 -0
- /package/dist/{protocol → types/protocol}/snapshot/snapshot-registry.d.ts +0 -0
- /package/dist/{protocol → types/protocol}/snapshot/snapshot.d.ts +0 -0
- /package/dist/{protocol/intent/intent.js → webgpu/types/2d/animation.test.d.ts} +0 -0
|
@@ -1,1047 +0,0 @@
|
|
|
1
|
-
type ObstacleId = number;
|
|
2
|
-
|
|
3
|
-
export type Obstacle =
|
|
4
|
-
| CircleObstacle
|
|
5
|
-
| RectObstacle
|
|
6
|
-
| PolygonObstacle;
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* NavMesh configuration options
|
|
10
|
-
*/
|
|
11
|
-
export interface NavMeshOptions<TWorkers extends boolean | 'auto' = false> {
|
|
12
|
-
/**
|
|
13
|
-
* Enable Web Workers for pathfinding
|
|
14
|
-
*
|
|
15
|
-
* - `false` (default): Synchronous pathfinding on main thread
|
|
16
|
-
* - `true`: Always use worker pool (4 workers)
|
|
17
|
-
* - `'auto'`: Automatically use workers when beneficial (>= 20 pending paths)
|
|
18
|
-
*
|
|
19
|
-
* @default false
|
|
20
|
-
*
|
|
21
|
-
* @remarks
|
|
22
|
-
* Workers provide 3-4.5x speedup for parallel pathfinding (20+ concurrent requests).
|
|
23
|
-
* For single/sequential pathfinding, sync is faster due to message passing overhead (~0.5ms).
|
|
24
|
-
*
|
|
25
|
-
* Use 'auto' for games where pathfinding load varies (e.g., RTS with unit groups).
|
|
26
|
-
*/
|
|
27
|
-
workers?: TWorkers;
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Number of workers to spawn (only used when workers = true)
|
|
31
|
-
* @default 4
|
|
32
|
-
*/
|
|
33
|
-
workerPoolSize?: number;
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Path to worker script (required if workers = true and running in browser)
|
|
37
|
-
* For Node.js/Bun, this is handled automatically
|
|
38
|
-
* @example './navmesh.worker.js'
|
|
39
|
-
*/
|
|
40
|
-
workerPath?: string;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Helper type to determine return type based on worker configuration
|
|
45
|
-
*/
|
|
46
|
-
type PathResult<TWorkers extends boolean | 'auto' | undefined> =
|
|
47
|
-
TWorkers extends false | undefined
|
|
48
|
-
? Vec2[] // Sync only
|
|
49
|
-
: TWorkers extends true
|
|
50
|
-
? Promise<Vec2[]> // Always async
|
|
51
|
-
: Vec2[] | Promise<Vec2[]>; // Auto: can be either
|
|
52
|
-
|
|
53
|
-
export type ObstacleInput =
|
|
54
|
-
| Omit<CircleObstacle, 'id'>
|
|
55
|
-
| Omit<RectObstacle, 'id'>
|
|
56
|
-
| Omit<PolygonObstacle, 'id'>;
|
|
57
|
-
|
|
58
|
-
interface Vec2 {
|
|
59
|
-
x: number;
|
|
60
|
-
y: number;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
interface BaseObstacle {
|
|
64
|
-
id: ObstacleId;
|
|
65
|
-
type: 'circle' | 'rect' | 'polygon';
|
|
66
|
-
solid?: boolean; // default true
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
export interface CircleObstacle extends BaseObstacle {
|
|
70
|
-
type: 'circle';
|
|
71
|
-
pos: Vec2;
|
|
72
|
-
radius: number;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
export interface RectObstacle extends BaseObstacle {
|
|
76
|
-
type: 'rect';
|
|
77
|
-
pos: Vec2; // bottom-left corner
|
|
78
|
-
size: Vec2;
|
|
79
|
-
rotation?: number; // radians, around center
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Polygon obstacle.
|
|
84
|
-
* IMPORTANT: `points` must be defined relative to local origin (0,0).
|
|
85
|
-
* The polygon will be rotated around (0,0) then translated to `pos`.
|
|
86
|
-
* Do NOT define points in world space.
|
|
87
|
-
*/
|
|
88
|
-
export interface PolygonObstacle extends BaseObstacle {
|
|
89
|
-
type: 'polygon';
|
|
90
|
-
points: Vec2[]; // MUST be relative to (0,0)
|
|
91
|
-
pos: Vec2; // world position
|
|
92
|
-
rotation?: number; // radians, around local origin (0,0)
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/* ---------------------------------- */
|
|
96
|
-
/* Utils */
|
|
97
|
-
/* ---------------------------------- */
|
|
98
|
-
|
|
99
|
-
const dirs = [
|
|
100
|
-
{ x: 1, y: 0 },
|
|
101
|
-
{ x: -1, y: 0 },
|
|
102
|
-
{ x: 0, y: 1 },
|
|
103
|
-
{ x: 0, y: -1 },
|
|
104
|
-
];
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* Converts world coordinates to grid cell coordinates (floor).
|
|
108
|
-
*/
|
|
109
|
-
const toCell = (v: Vec2): Vec2 => ({
|
|
110
|
-
x: Math.floor(v.x),
|
|
111
|
-
y: Math.floor(v.y),
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Converts grid cell coordinates to world coordinates (cell center).
|
|
116
|
-
*/
|
|
117
|
-
const fromCell = (v: Vec2): Vec2 => ({
|
|
118
|
-
x: v.x + 0.5,
|
|
119
|
-
y: v.y + 0.5
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* Generates unique sequential IDs for obstacles.
|
|
124
|
-
*/
|
|
125
|
-
const genId = (() => {
|
|
126
|
-
let i = 1;
|
|
127
|
-
return () => i++;
|
|
128
|
-
})();
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* Encodes grid coordinates as a single integer for use as Map/Set keys.
|
|
132
|
-
* Uses 32-bit safe packing: lower 16 bits = x, upper 16 bits = y.
|
|
133
|
-
* Supports coordinates in range [-32768, 32767].
|
|
134
|
-
*/
|
|
135
|
-
const encodeCell = (x: number, y: number): number =>
|
|
136
|
-
(x & 0xffff) | ((y & 0xffff) << 16);
|
|
137
|
-
|
|
138
|
-
/**
|
|
139
|
-
* Decodes an encoded cell back to {x, y}.
|
|
140
|
-
* Properly handles sign extension for negative coordinates.
|
|
141
|
-
*/
|
|
142
|
-
const decodeCell = (n: number): Vec2 => ({
|
|
143
|
-
x: (n << 16) >> 16,
|
|
144
|
-
y: n >> 16,
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
/* ---------------------------------- */
|
|
148
|
-
/* Binary Heap (Priority Queue) */
|
|
149
|
-
/* ---------------------------------- */
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* Min-heap priority queue for A*.
|
|
153
|
-
* Supports O(log n) insert and extract-min operations.
|
|
154
|
-
*/
|
|
155
|
-
class BinaryHeap<T> {
|
|
156
|
-
private heap: T[] = [];
|
|
157
|
-
|
|
158
|
-
constructor(private scoreFn: (item: T) => number) {}
|
|
159
|
-
|
|
160
|
-
push(item: T) {
|
|
161
|
-
this.heap.push(item);
|
|
162
|
-
this.bubbleUp(this.heap.length - 1);
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
pop(): T | undefined {
|
|
166
|
-
const result = this.heap[0];
|
|
167
|
-
const end = this.heap.pop();
|
|
168
|
-
|
|
169
|
-
if (this.heap.length > 0 && end !== undefined) {
|
|
170
|
-
this.heap[0] = end;
|
|
171
|
-
this.sinkDown(0);
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
return result;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
get size(): number {
|
|
178
|
-
return this.heap.length;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
private bubbleUp(n: number) {
|
|
182
|
-
const element = this.heap[n];
|
|
183
|
-
const score = this.scoreFn(element);
|
|
184
|
-
|
|
185
|
-
while (n > 0) {
|
|
186
|
-
const parentN = ((n + 1) >> 1) - 1;
|
|
187
|
-
const parent = this.heap[parentN];
|
|
188
|
-
|
|
189
|
-
if (score >= this.scoreFn(parent)) break;
|
|
190
|
-
|
|
191
|
-
this.heap[parentN] = element;
|
|
192
|
-
this.heap[n] = parent;
|
|
193
|
-
n = parentN;
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
private sinkDown(n: number) {
|
|
198
|
-
const length = this.heap.length;
|
|
199
|
-
const element = this.heap[n];
|
|
200
|
-
const elemScore = this.scoreFn(element);
|
|
201
|
-
|
|
202
|
-
while (true) {
|
|
203
|
-
const child2N = (n + 1) << 1;
|
|
204
|
-
const child1N = child2N - 1;
|
|
205
|
-
let swap: number | null = null;
|
|
206
|
-
let child1Score: number;
|
|
207
|
-
|
|
208
|
-
if (child1N < length) {
|
|
209
|
-
const child1 = this.heap[child1N];
|
|
210
|
-
child1Score = this.scoreFn(child1);
|
|
211
|
-
if (child1Score < elemScore) {
|
|
212
|
-
swap = child1N;
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
if (child2N < length) {
|
|
217
|
-
const child2 = this.heap[child2N];
|
|
218
|
-
const child2Score = this.scoreFn(child2);
|
|
219
|
-
if (child2Score < (swap === null ? elemScore : child1Score!)) {
|
|
220
|
-
swap = child2N;
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
if (swap === null) break;
|
|
225
|
-
|
|
226
|
-
this.heap[n] = this.heap[swap];
|
|
227
|
-
this.heap[swap] = element;
|
|
228
|
-
n = swap;
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
/* ---------------------------------- */
|
|
234
|
-
/* Spatial Hash */
|
|
235
|
-
/* ---------------------------------- */
|
|
236
|
-
|
|
237
|
-
/**
|
|
238
|
-
* Spatial hash for fast obstacle queries.
|
|
239
|
-
* Divides space into fixed-size cells and indexes obstacles by cell.
|
|
240
|
-
* Provides O(1) average case lookup instead of O(n) linear scan.
|
|
241
|
-
*
|
|
242
|
-
* Cell size = 1 matches grid pathfinding unit cells.
|
|
243
|
-
*/
|
|
244
|
-
class SpatialHash {
|
|
245
|
-
private cellSize: number;
|
|
246
|
-
private grid = new Map<number, Set<ObstacleId>>();
|
|
247
|
-
private obstacleCells = new Map<ObstacleId, Set<number>>();
|
|
248
|
-
|
|
249
|
-
constructor(cellSize = 1) {
|
|
250
|
-
this.cellSize = cellSize;
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
/**
|
|
254
|
-
* Returns hash key for a world position.
|
|
255
|
-
*/
|
|
256
|
-
private hash(x: number, y: number): number {
|
|
257
|
-
const cx = Math.floor(x / this.cellSize);
|
|
258
|
-
const cy = Math.floor(y / this.cellSize);
|
|
259
|
-
return encodeCell(cx, cy);
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
/**
|
|
263
|
-
* Adds an obstacle to the spatial hash.
|
|
264
|
-
*/
|
|
265
|
-
add(id: ObstacleId, obstacle: Obstacle) {
|
|
266
|
-
const cells = this.getCellsForObstacle(obstacle);
|
|
267
|
-
|
|
268
|
-
for (const cell of cells) {
|
|
269
|
-
if (!this.grid.has(cell)) {
|
|
270
|
-
this.grid.set(cell, new Set());
|
|
271
|
-
}
|
|
272
|
-
this.grid.get(cell)!.add(id);
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
this.obstacleCells.set(id, cells);
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
/**
|
|
279
|
-
* Removes an obstacle from the spatial hash.
|
|
280
|
-
*/
|
|
281
|
-
remove(id: ObstacleId) {
|
|
282
|
-
const cells = this.obstacleCells.get(id);
|
|
283
|
-
if (!cells) return;
|
|
284
|
-
|
|
285
|
-
for (const cell of cells) {
|
|
286
|
-
const bucket = this.grid.get(cell);
|
|
287
|
-
if (bucket) {
|
|
288
|
-
bucket.delete(id);
|
|
289
|
-
if (bucket.size === 0) {
|
|
290
|
-
this.grid.delete(cell);
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
this.obstacleCells.delete(id);
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
/**
|
|
299
|
-
* Returns obstacle IDs that might contain the given point.
|
|
300
|
-
*/
|
|
301
|
-
query(pos: Vec2): Set<ObstacleId> {
|
|
302
|
-
const cell = this.hash(pos.x, pos.y);
|
|
303
|
-
return this.grid.get(cell) || new Set();
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
/**
|
|
307
|
-
* Clears the entire spatial hash.
|
|
308
|
-
*/
|
|
309
|
-
clear() {
|
|
310
|
-
this.grid.clear();
|
|
311
|
-
this.obstacleCells.clear();
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
/**
|
|
315
|
-
* Determines which cells an obstacle overlaps.
|
|
316
|
-
*/
|
|
317
|
-
private getCellsForObstacle(o: Obstacle): Set<number> {
|
|
318
|
-
const cells = new Set<number>();
|
|
319
|
-
|
|
320
|
-
if (o.type === 'circle') {
|
|
321
|
-
const r = o.radius;
|
|
322
|
-
const minX = Math.floor((o.pos.x - r) / this.cellSize);
|
|
323
|
-
const maxX = Math.floor((o.pos.x + r) / this.cellSize);
|
|
324
|
-
const minY = Math.floor((o.pos.y - r) / this.cellSize);
|
|
325
|
-
const maxY = Math.floor((o.pos.y + r) / this.cellSize);
|
|
326
|
-
|
|
327
|
-
for (let x = minX; x <= maxX; x++) {
|
|
328
|
-
for (let y = minY; y <= maxY; y++) {
|
|
329
|
-
cells.add(encodeCell(x, y));
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
} else if (o.type === 'rect') {
|
|
333
|
-
const cx = o.pos.x + o.size.x / 2;
|
|
334
|
-
const cy = o.pos.y + o.size.y / 2;
|
|
335
|
-
const hw = o.size.x / 2;
|
|
336
|
-
const hh = o.size.y / 2;
|
|
337
|
-
const diagonal = Math.sqrt(hw * hw + hh * hh);
|
|
338
|
-
|
|
339
|
-
const minX = Math.floor((cx - diagonal) / this.cellSize);
|
|
340
|
-
const maxX = Math.floor((cx + diagonal) / this.cellSize);
|
|
341
|
-
const minY = Math.floor((cy - diagonal) / this.cellSize);
|
|
342
|
-
const maxY = Math.floor((cy + diagonal) / this.cellSize);
|
|
343
|
-
|
|
344
|
-
for (let x = minX; x <= maxX; x++) {
|
|
345
|
-
for (let y = minY; y <= maxY; y++) {
|
|
346
|
-
cells.add(encodeCell(x, y));
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
} else if (o.type === 'polygon') {
|
|
350
|
-
const bounds = getPolygonBounds(o);
|
|
351
|
-
const minX = Math.floor(bounds.minX / this.cellSize);
|
|
352
|
-
const maxX = Math.floor(bounds.maxX / this.cellSize);
|
|
353
|
-
const minY = Math.floor(bounds.minY / this.cellSize);
|
|
354
|
-
const maxY = Math.floor(bounds.maxY / this.cellSize);
|
|
355
|
-
|
|
356
|
-
for (let x = minX; x <= maxX; x++) {
|
|
357
|
-
for (let y = minY; y <= maxY; y++) {
|
|
358
|
-
cells.add(encodeCell(x, y));
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
return cells;
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
/* ---------------------------------- */
|
|
368
|
-
/* Geometry helpers */
|
|
369
|
-
/* ---------------------------------- */
|
|
370
|
-
|
|
371
|
-
/**
|
|
372
|
-
* Tests if a point is inside a circle obstacle.
|
|
373
|
-
*/
|
|
374
|
-
function pointInCircle(p: Vec2, c: CircleObstacle): boolean {
|
|
375
|
-
const dx = p.x - c.pos.x;
|
|
376
|
-
const dy = p.y - c.pos.y;
|
|
377
|
-
return dx * dx + dy * dy <= c.radius * c.radius;
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
/**
|
|
381
|
-
* Tests if a point is inside a rectangle obstacle.
|
|
382
|
-
* Handles rotation around the rectangle's center.
|
|
383
|
-
*/
|
|
384
|
-
function pointInRect(p: Vec2, r: RectObstacle): boolean {
|
|
385
|
-
const cx = r.pos.x + r.size.x / 2;
|
|
386
|
-
const cy = r.pos.y + r.size.y / 2;
|
|
387
|
-
|
|
388
|
-
if (r.rotation) {
|
|
389
|
-
const cos = Math.cos(-r.rotation);
|
|
390
|
-
const sin = Math.sin(-r.rotation);
|
|
391
|
-
const dx = p.x - cx;
|
|
392
|
-
const dy = p.y - cy;
|
|
393
|
-
const localX = dx * cos - dy * sin;
|
|
394
|
-
const localY = dx * sin + dy * cos;
|
|
395
|
-
|
|
396
|
-
return Math.abs(localX) <= r.size.x / 2 &&
|
|
397
|
-
Math.abs(localY) <= r.size.y / 2;
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
return (
|
|
401
|
-
p.x >= r.pos.x &&
|
|
402
|
-
p.y >= r.pos.y &&
|
|
403
|
-
p.x <= r.pos.x + r.size.x &&
|
|
404
|
-
p.y <= r.pos.y + r.size.y
|
|
405
|
-
);
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
/**
|
|
409
|
-
* Tests if a point is inside a polygon obstacle using ray casting.
|
|
410
|
-
* REQUIRES: polygon.points are defined relative to local origin (0,0).
|
|
411
|
-
* Uses numerically stable intersection test.
|
|
412
|
-
*/
|
|
413
|
-
function pointInPolygon(p: Vec2, poly: PolygonObstacle): boolean {
|
|
414
|
-
let inside = false;
|
|
415
|
-
const pts = poly.points;
|
|
416
|
-
const cos = poly.rotation ? Math.cos(poly.rotation) : 1;
|
|
417
|
-
const sin = poly.rotation ? Math.sin(poly.rotation) : 0;
|
|
418
|
-
|
|
419
|
-
for (let i = 0, j = pts.length - 1; i < pts.length; j = i++) {
|
|
420
|
-
let xi = pts[i].x;
|
|
421
|
-
let yi = pts[i].y;
|
|
422
|
-
let xj = pts[j].x;
|
|
423
|
-
let yj = pts[j].y;
|
|
424
|
-
|
|
425
|
-
if (poly.rotation) {
|
|
426
|
-
const tempXi = xi * cos - yi * sin;
|
|
427
|
-
const tempYi = xi * sin + yi * cos;
|
|
428
|
-
const tempXj = xj * cos - yj * sin;
|
|
429
|
-
const tempYj = xj * sin + yj * cos;
|
|
430
|
-
xi = tempXi;
|
|
431
|
-
yi = tempYi;
|
|
432
|
-
xj = tempXj;
|
|
433
|
-
yj = tempYj;
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
xi += poly.pos.x;
|
|
437
|
-
yi += poly.pos.y;
|
|
438
|
-
xj += poly.pos.x;
|
|
439
|
-
yj += poly.pos.y;
|
|
440
|
-
|
|
441
|
-
// Stable ray casting test
|
|
442
|
-
const intersect =
|
|
443
|
-
(yi > p.y) !== (yj > p.y) &&
|
|
444
|
-
p.x < ((xj - xi) * (p.y - yi)) / (yj - yi) + xi;
|
|
445
|
-
|
|
446
|
-
if (intersect) inside = !inside;
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
return inside;
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
/**
|
|
453
|
-
* Computes axis-aligned bounding box for a transformed polygon.
|
|
454
|
-
*/
|
|
455
|
-
function getPolygonBounds(poly: PolygonObstacle): {
|
|
456
|
-
minX: number;
|
|
457
|
-
minY: number;
|
|
458
|
-
maxX: number;
|
|
459
|
-
maxY: number;
|
|
460
|
-
} {
|
|
461
|
-
let minX = Infinity, minY = Infinity;
|
|
462
|
-
let maxX = -Infinity, maxY = -Infinity;
|
|
463
|
-
|
|
464
|
-
const cos = poly.rotation ? Math.cos(poly.rotation) : 1;
|
|
465
|
-
const sin = poly.rotation ? Math.sin(poly.rotation) : 0;
|
|
466
|
-
|
|
467
|
-
for (const p of poly.points) {
|
|
468
|
-
let x = p.x;
|
|
469
|
-
let y = p.y;
|
|
470
|
-
|
|
471
|
-
if (poly.rotation) {
|
|
472
|
-
const tx = x * cos - y * sin;
|
|
473
|
-
const ty = x * sin + y * cos;
|
|
474
|
-
x = tx;
|
|
475
|
-
y = ty;
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
x += poly.pos.x;
|
|
479
|
-
y += poly.pos.y;
|
|
480
|
-
|
|
481
|
-
minX = Math.min(minX, x);
|
|
482
|
-
minY = Math.min(minY, y);
|
|
483
|
-
maxX = Math.max(maxX, x);
|
|
484
|
-
maxY = Math.max(maxY, y);
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
return { minX, minY, maxX, maxY };
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
/* ---------------------------------- */
|
|
491
|
-
/* Obstacles */
|
|
492
|
-
/* ---------------------------------- */
|
|
493
|
-
|
|
494
|
-
/**
|
|
495
|
-
* Manages obstacles with spatial hashing for fast queries.
|
|
496
|
-
* Version tracking prevents unnecessary rebuilds.
|
|
497
|
-
*/
|
|
498
|
-
class Obstacles {
|
|
499
|
-
private items = new Map<ObstacleId, Obstacle>();
|
|
500
|
-
private spatial = new SpatialHash(1); // Match grid cell size
|
|
501
|
-
private _cachedItems: Obstacle[] = [];
|
|
502
|
-
dirty = true;
|
|
503
|
-
version = 0;
|
|
504
|
-
|
|
505
|
-
add(obstacle: ObstacleInput): ObstacleId {
|
|
506
|
-
const id = genId();
|
|
507
|
-
const newObstacle = { ...obstacle, id } as Obstacle;
|
|
508
|
-
this.items.set(id, newObstacle);
|
|
509
|
-
this.spatial.add(id, newObstacle);
|
|
510
|
-
this.dirty = true;
|
|
511
|
-
this.version++;
|
|
512
|
-
return id;
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
move(id: ObstacleId, pos: Vec2) {
|
|
516
|
-
const o = this.items.get(id);
|
|
517
|
-
if (!o) return;
|
|
518
|
-
|
|
519
|
-
// Remove from old position in spatial hash
|
|
520
|
-
this.spatial.remove(id);
|
|
521
|
-
|
|
522
|
-
// Create updated obstacle
|
|
523
|
-
const updated = {
|
|
524
|
-
...o,
|
|
525
|
-
pos: { ...pos },
|
|
526
|
-
};
|
|
527
|
-
|
|
528
|
-
this.items.set(id, updated as Obstacle);
|
|
529
|
-
this.spatial.add(id, updated as Obstacle);
|
|
530
|
-
this.dirty = true;
|
|
531
|
-
this.version++;
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
remove(id: ObstacleId) {
|
|
535
|
-
this.spatial.remove(id);
|
|
536
|
-
this.items.delete(id);
|
|
537
|
-
this.dirty = true;
|
|
538
|
-
this.version++;
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
/**
|
|
542
|
-
* Fast spatial query using hash grid.
|
|
543
|
-
* O(1) average case instead of O(n) linear scan.
|
|
544
|
-
*/
|
|
545
|
-
at(pos: Vec2): Obstacle | undefined {
|
|
546
|
-
const candidates = this.spatial.query(pos);
|
|
547
|
-
|
|
548
|
-
for (const id of candidates) {
|
|
549
|
-
const o = this.items.get(id);
|
|
550
|
-
if (!o || o.solid === false) continue;
|
|
551
|
-
|
|
552
|
-
if (o.type === 'circle' && pointInCircle(pos, o)) return o;
|
|
553
|
-
if (o.type === 'rect' && pointInRect(pos, o)) return o;
|
|
554
|
-
if (o.type === 'polygon' && pointInPolygon(pos, o)) return o;
|
|
555
|
-
}
|
|
556
|
-
}
|
|
557
|
-
|
|
558
|
-
get values(): Obstacle[] {
|
|
559
|
-
if (!this.dirty) return this._cachedItems;
|
|
560
|
-
this._cachedItems = [...this.items.values()];
|
|
561
|
-
this.dirty = false;
|
|
562
|
-
return this._cachedItems;
|
|
563
|
-
}
|
|
564
|
-
}
|
|
565
|
-
|
|
566
|
-
/* ---------------------------------- */
|
|
567
|
-
/* Grid Nav */
|
|
568
|
-
/* ---------------------------------- */
|
|
569
|
-
|
|
570
|
-
/**
|
|
571
|
-
* Grid navigation with simple full rebuild.
|
|
572
|
-
* Fast enough for most games (< 1000 obstacles).
|
|
573
|
-
*
|
|
574
|
-
* Performance: O(n * area) where n = obstacle count.
|
|
575
|
-
* Typical rebuild time: < 1ms for 100 obstacles on 100x100 grid.
|
|
576
|
-
*/
|
|
577
|
-
class GridNav {
|
|
578
|
-
private blocked = new Set<number>();
|
|
579
|
-
|
|
580
|
-
constructor(private obstacles: Obstacles) {}
|
|
581
|
-
|
|
582
|
-
/**
|
|
583
|
-
* Rebuilds the entire blocked cell set.
|
|
584
|
-
* Simple and correct - no incremental complexity.
|
|
585
|
-
*/
|
|
586
|
-
rebuild() {
|
|
587
|
-
this.blocked.clear();
|
|
588
|
-
|
|
589
|
-
for (const o of this.obstacles.values) {
|
|
590
|
-
if (o.solid === false) continue;
|
|
591
|
-
|
|
592
|
-
if (o.type === 'circle') {
|
|
593
|
-
const r = Math.ceil(o.radius);
|
|
594
|
-
const cx = Math.floor(o.pos.x);
|
|
595
|
-
const cy = Math.floor(o.pos.y);
|
|
596
|
-
|
|
597
|
-
for (let dx = -r; dx <= r; dx++) {
|
|
598
|
-
for (let dy = -r; dy <= r; dy++) {
|
|
599
|
-
const cellX = cx + dx;
|
|
600
|
-
const cellY = cy + dy;
|
|
601
|
-
const cellCenter = { x: cellX + 0.5, y: cellY + 0.5 };
|
|
602
|
-
if (pointInCircle(cellCenter, o)) {
|
|
603
|
-
this.blocked.add(encodeCell(cellX, cellY));
|
|
604
|
-
}
|
|
605
|
-
}
|
|
606
|
-
}
|
|
607
|
-
} else if (o.type === 'rect') {
|
|
608
|
-
const cx = o.pos.x + o.size.x / 2;
|
|
609
|
-
const cy = o.pos.y + o.size.y / 2;
|
|
610
|
-
const hw = o.size.x / 2;
|
|
611
|
-
const hh = o.size.y / 2;
|
|
612
|
-
const diagonal = Math.sqrt(hw * hw + hh * hh);
|
|
613
|
-
|
|
614
|
-
const minX = Math.floor(cx - diagonal);
|
|
615
|
-
const maxX = Math.ceil(cx + diagonal);
|
|
616
|
-
const minY = Math.floor(cy - diagonal);
|
|
617
|
-
const maxY = Math.ceil(cy + diagonal);
|
|
618
|
-
|
|
619
|
-
for (let x = minX; x <= maxX; x++) {
|
|
620
|
-
for (let y = minY; y <= maxY; y++) {
|
|
621
|
-
const cellCenter = { x: x + 0.5, y: y + 0.5 };
|
|
622
|
-
if (pointInRect(cellCenter, o)) {
|
|
623
|
-
this.blocked.add(encodeCell(x, y));
|
|
624
|
-
}
|
|
625
|
-
}
|
|
626
|
-
}
|
|
627
|
-
} else if (o.type === 'polygon') {
|
|
628
|
-
const bounds = getPolygonBounds(o);
|
|
629
|
-
const minX = Math.floor(bounds.minX);
|
|
630
|
-
const maxX = Math.ceil(bounds.maxX);
|
|
631
|
-
const minY = Math.floor(bounds.minY);
|
|
632
|
-
const maxY = Math.ceil(bounds.maxY);
|
|
633
|
-
|
|
634
|
-
for (let x = minX; x <= maxX; x++) {
|
|
635
|
-
for (let y = minY; y <= maxY; y++) {
|
|
636
|
-
const cellCenter = { x: x + 0.5, y: y + 0.5 };
|
|
637
|
-
if (pointInPolygon(cellCenter, o)) {
|
|
638
|
-
this.blocked.add(encodeCell(x, y));
|
|
639
|
-
}
|
|
640
|
-
}
|
|
641
|
-
}
|
|
642
|
-
}
|
|
643
|
-
}
|
|
644
|
-
}
|
|
645
|
-
|
|
646
|
-
findPath(from: Vec2, to: Vec2): Vec2[] {
|
|
647
|
-
return aStar(
|
|
648
|
-
toCell(from),
|
|
649
|
-
toCell(to),
|
|
650
|
-
(x, y) => !this.blocked.has(encodeCell(x, y))
|
|
651
|
-
).map(fromCell);
|
|
652
|
-
}
|
|
653
|
-
}
|
|
654
|
-
|
|
655
|
-
/* ---------------------------------- */
|
|
656
|
-
/* Graph Nav */
|
|
657
|
-
/* ---------------------------------- */
|
|
658
|
-
|
|
659
|
-
/**
|
|
660
|
-
* Line-of-sight navigation with grid fallback.
|
|
661
|
-
* Not a true navmesh - use GridNav for production.
|
|
662
|
-
*/
|
|
663
|
-
class GraphNav {
|
|
664
|
-
constructor(private obstacles: Obstacles) {}
|
|
665
|
-
|
|
666
|
-
rebuild() {}
|
|
667
|
-
|
|
668
|
-
findPath(from: Vec2, to: Vec2): Vec2[] {
|
|
669
|
-
// Uniform sampling along path for LOS check
|
|
670
|
-
const steps = Math.ceil(
|
|
671
|
-
Math.hypot(to.x - from.x, to.y - from.y) * 2
|
|
672
|
-
);
|
|
673
|
-
|
|
674
|
-
let blocked = false;
|
|
675
|
-
|
|
676
|
-
for (let i = 1; i <= steps; i++) {
|
|
677
|
-
const t = i / steps;
|
|
678
|
-
const p = {
|
|
679
|
-
x: from.x + (to.x - from.x) * t,
|
|
680
|
-
y: from.y + (to.y - from.y) * t,
|
|
681
|
-
};
|
|
682
|
-
|
|
683
|
-
if (this.obstacles.at(p)) {
|
|
684
|
-
blocked = true;
|
|
685
|
-
break;
|
|
686
|
-
}
|
|
687
|
-
}
|
|
688
|
-
|
|
689
|
-
if (!blocked) {
|
|
690
|
-
return [from, to];
|
|
691
|
-
}
|
|
692
|
-
|
|
693
|
-
// Fallback to grid A*
|
|
694
|
-
const cellPath = aStar(
|
|
695
|
-
toCell(from),
|
|
696
|
-
toCell(to),
|
|
697
|
-
(x, y) => {
|
|
698
|
-
const p = { x: x + 0.5, y: y + 0.5 };
|
|
699
|
-
return !this.obstacles.at(p);
|
|
700
|
-
}
|
|
701
|
-
);
|
|
702
|
-
|
|
703
|
-
return cellPath.map(fromCell);
|
|
704
|
-
}
|
|
705
|
-
}
|
|
706
|
-
|
|
707
|
-
/* ---------------------------------- */
|
|
708
|
-
/* NavMesh */
|
|
709
|
-
/* ---------------------------------- */
|
|
710
|
-
|
|
711
|
-
type NavType = 'grid' | 'graph';
|
|
712
|
-
|
|
713
|
-
/**
|
|
714
|
-
* Navigation mesh with spatial hashing and smart rebuild.
|
|
715
|
-
*
|
|
716
|
-
* Features:
|
|
717
|
-
* - Spatial hash: O(1) obstacle queries
|
|
718
|
-
* - Version tracking: zero unnecessary rebuilds
|
|
719
|
-
* - Binary heap A*: handles 10k+ node searches
|
|
720
|
-
* - Simple full rebuild: correct and fast enough
|
|
721
|
-
* - Optional Web Workers: 3-4.5x speedup for parallel pathfinding
|
|
722
|
-
*
|
|
723
|
-
* Performance characteristics:
|
|
724
|
-
* - Obstacle query: O(1) average via spatial hash
|
|
725
|
-
* - Grid rebuild: O(n * area), < 1ms for typical games
|
|
726
|
-
* - Pathfinding: O(b^d * log n) with binary heap
|
|
727
|
-
* - Worker overhead: ~0.5ms per request (use for 20+ concurrent paths)
|
|
728
|
-
*
|
|
729
|
-
* Production ready for:
|
|
730
|
-
* - Grid-based games
|
|
731
|
-
* - RTS with < 1000 dynamic obstacles
|
|
732
|
-
* - Turn-based games
|
|
733
|
-
* - Moderate map sizes (< 1M cells)
|
|
734
|
-
*
|
|
735
|
-
* @example
|
|
736
|
-
* ```ts
|
|
737
|
-
* // Simple usage (synchronous) - typed as Vec2[]
|
|
738
|
-
* const navmesh = new NavMesh('grid');
|
|
739
|
-
* const path = navmesh.findPath({ from: {x:0, y:0}, to: {x:10, y:10} });
|
|
740
|
-
*
|
|
741
|
-
* // With workers (automatic) - typed as Vec2[] | Promise<Vec2[]>
|
|
742
|
-
* const navmesh = new NavMesh('grid', { workers: 'auto' });
|
|
743
|
-
* const path = await navmesh.findPath({ from: {x:0, y:0}, to: {x:10, y:10} });
|
|
744
|
-
*
|
|
745
|
-
* // With workers (always) - typed as Promise<Vec2[]>
|
|
746
|
-
* const navmesh = new NavMesh('grid', { workers: true });
|
|
747
|
-
* const path = await navmesh.findPath({ from: {x:0, y:0}, to: {x:10, y:10} });
|
|
748
|
-
* ```
|
|
749
|
-
*/
|
|
750
|
-
export class NavMesh<TWorkers extends boolean | 'auto' = false> {
|
|
751
|
-
private grid?: GridNav;
|
|
752
|
-
private graph?: GraphNav;
|
|
753
|
-
private lastVersion = -1;
|
|
754
|
-
obstacles: Obstacles;
|
|
755
|
-
|
|
756
|
-
// Worker support
|
|
757
|
-
private options: Required<NavMeshOptions<TWorkers>>;
|
|
758
|
-
private workerPool?: any; // Lazy-loaded NavMeshWorkerPool
|
|
759
|
-
private pendingPaths = 0;
|
|
760
|
-
private readonly AUTO_WORKER_THRESHOLD = 20; // Use workers when >= 20 pending paths
|
|
761
|
-
|
|
762
|
-
constructor(
|
|
763
|
-
private type: NavType,
|
|
764
|
-
options?: NavMeshOptions<TWorkers>
|
|
765
|
-
) {
|
|
766
|
-
this.obstacles = new Obstacles();
|
|
767
|
-
|
|
768
|
-
// Set defaults - cast to any to avoid complex type gymnastics
|
|
769
|
-
this.options = {
|
|
770
|
-
workers: (options?.workers ?? false) as any,
|
|
771
|
-
workerPoolSize: options?.workerPoolSize ?? 4,
|
|
772
|
-
workerPath: options?.workerPath ?? './navmesh.worker.js',
|
|
773
|
-
};
|
|
774
|
-
|
|
775
|
-
// Initialize sync navigation
|
|
776
|
-
if (type === 'grid') this.grid = new GridNav(this.obstacles);
|
|
777
|
-
if (type === 'graph') this.graph = new GraphNav(this.obstacles);
|
|
778
|
-
|
|
779
|
-
// Initialize worker pool if workers = true
|
|
780
|
-
if (this.options.workers === true) {
|
|
781
|
-
this.initWorkerPool();
|
|
782
|
-
}
|
|
783
|
-
}
|
|
784
|
-
|
|
785
|
-
/**
|
|
786
|
-
* Lazy initialize worker pool
|
|
787
|
-
*/
|
|
788
|
-
private async initWorkerPool() {
|
|
789
|
-
if (this.workerPool) return;
|
|
790
|
-
|
|
791
|
-
try {
|
|
792
|
-
// Dynamic import to avoid bundling worker pool if not needed
|
|
793
|
-
const { NavMeshWorkerPool } = await import('./navmesh-worker-pool');
|
|
794
|
-
|
|
795
|
-
this.workerPool = new NavMeshWorkerPool(
|
|
796
|
-
this.options.workerPoolSize,
|
|
797
|
-
this.options.workerPath,
|
|
798
|
-
this.type,
|
|
799
|
-
this.obstacles.values
|
|
800
|
-
);
|
|
801
|
-
|
|
802
|
-
await this.workerPool.init();
|
|
803
|
-
} catch (error) {
|
|
804
|
-
console.warn('Failed to initialize worker pool, falling back to sync:', error);
|
|
805
|
-
this.options.workers = false as any; // Disable workers on failure
|
|
806
|
-
}
|
|
807
|
-
}
|
|
808
|
-
|
|
809
|
-
/**
|
|
810
|
-
* Check if we should use workers for this request
|
|
811
|
-
*/
|
|
812
|
-
private shouldUseWorkers(): boolean {
|
|
813
|
-
if (this.options.workers === false) return false;
|
|
814
|
-
if (this.options.workers === true) return true;
|
|
815
|
-
|
|
816
|
-
// Auto mode: use workers if we have many pending paths
|
|
817
|
-
return this.pendingPaths >= this.AUTO_WORKER_THRESHOLD;
|
|
818
|
-
}
|
|
819
|
-
|
|
820
|
-
/**
|
|
821
|
-
* Adds an obstacle and returns its unique ID.
|
|
822
|
-
* For polygons: ensure points are defined relative to (0,0).
|
|
823
|
-
*/
|
|
824
|
-
addObstacle(obstacle: ObstacleInput): ObstacleId {
|
|
825
|
-
return this.obstacles.add(obstacle);
|
|
826
|
-
}
|
|
827
|
-
|
|
828
|
-
/**
|
|
829
|
-
* Moves an existing obstacle to a new position.
|
|
830
|
-
*/
|
|
831
|
-
moveObstacle(id: ObstacleId, pos: Vec2) {
|
|
832
|
-
this.obstacles.move(id, pos);
|
|
833
|
-
}
|
|
834
|
-
|
|
835
|
-
/**
|
|
836
|
-
* Removes an obstacle by ID.
|
|
837
|
-
*/
|
|
838
|
-
removeObstacle(id: ObstacleId) {
|
|
839
|
-
this.obstacles.remove(id);
|
|
840
|
-
}
|
|
841
|
-
|
|
842
|
-
/**
|
|
843
|
-
* Returns all current obstacles.
|
|
844
|
-
*/
|
|
845
|
-
getObstacles(): Obstacle[] {
|
|
846
|
-
return this.obstacles.values;
|
|
847
|
-
}
|
|
848
|
-
|
|
849
|
-
/**
|
|
850
|
-
* Finds a path from start to goal.
|
|
851
|
-
* Automatically rebuilds navigation data if obstacles changed.
|
|
852
|
-
* Returns empty array if no path exists.
|
|
853
|
-
*
|
|
854
|
-
* @remarks
|
|
855
|
-
* - If workers are disabled (false): Returns Vec2[] synchronously
|
|
856
|
-
* - If workers are enabled (true): Returns Promise<Vec2[]>
|
|
857
|
-
* - If workers are 'auto': Returns Vec2[] | Promise<Vec2[]> based on load
|
|
858
|
-
*
|
|
859
|
-
* @example
|
|
860
|
-
* ```ts
|
|
861
|
-
* // Synchronous (no workers) - typed as Vec2[]
|
|
862
|
-
* const navmesh = new NavMesh('grid');
|
|
863
|
-
* const path = navmesh.findPath({ from, to });
|
|
864
|
-
*
|
|
865
|
-
* // Asynchronous (with workers) - typed as Promise<Vec2[]>
|
|
866
|
-
* const navmesh = new NavMesh('grid', { workers: true });
|
|
867
|
-
* const path = await navmesh.findPath({ from, to });
|
|
868
|
-
*
|
|
869
|
-
* // Auto mode - typed as Vec2[] | Promise<Vec2[]>
|
|
870
|
-
* const navmesh = new NavMesh('grid', { workers: 'auto' });
|
|
871
|
-
* const result = navmesh.findPath({ from, to });
|
|
872
|
-
* const path = result instanceof Promise ? await result : result;
|
|
873
|
-
* ```
|
|
874
|
-
*/
|
|
875
|
-
findPath({ from, to }: { from: Vec2; to: Vec2 }): PathResult<TWorkers> {
|
|
876
|
-
// Check if we should use workers
|
|
877
|
-
if (this.shouldUseWorkers() && this.workerPool) {
|
|
878
|
-
// Async path (with workers)
|
|
879
|
-
this.pendingPaths++;
|
|
880
|
-
return this.findPathAsync(from, to).finally(() => {
|
|
881
|
-
this.pendingPaths--;
|
|
882
|
-
}) as PathResult<TWorkers>;
|
|
883
|
-
}
|
|
884
|
-
|
|
885
|
-
// Sync path (no workers)
|
|
886
|
-
this.rebuild();
|
|
887
|
-
return (this.type === 'grid'
|
|
888
|
-
? this.grid!.findPath(from, to)
|
|
889
|
-
: this.graph!.findPath(from, to)) as PathResult<TWorkers>;
|
|
890
|
-
}
|
|
891
|
-
|
|
892
|
-
/**
|
|
893
|
-
* Async pathfinding using worker pool
|
|
894
|
-
*/
|
|
895
|
-
private async findPathAsync(from: Vec2, to: Vec2): Promise<Vec2[]> {
|
|
896
|
-
// Lazy init for 'auto' mode
|
|
897
|
-
if (this.options.workers === 'auto' && !this.workerPool) {
|
|
898
|
-
await this.initWorkerPool();
|
|
899
|
-
}
|
|
900
|
-
|
|
901
|
-
if (!this.workerPool) {
|
|
902
|
-
// Fallback to sync if workers failed to init
|
|
903
|
-
this.rebuild();
|
|
904
|
-
return this.type === 'grid'
|
|
905
|
-
? this.grid!.findPath(from, to)
|
|
906
|
-
: this.graph!.findPath(from, to);
|
|
907
|
-
}
|
|
908
|
-
|
|
909
|
-
return this.workerPool.findPath(from, to);
|
|
910
|
-
}
|
|
911
|
-
|
|
912
|
-
/**
|
|
913
|
-
* Smart rebuild - only rebuilds if obstacles changed.
|
|
914
|
-
* Version checking eliminates unnecessary work.
|
|
915
|
-
*/
|
|
916
|
-
rebuild() {
|
|
917
|
-
if (this.lastVersion === this.obstacles.version) return;
|
|
918
|
-
|
|
919
|
-
this.grid?.rebuild();
|
|
920
|
-
this.graph?.rebuild();
|
|
921
|
-
|
|
922
|
-
this.lastVersion = this.obstacles.version;
|
|
923
|
-
}
|
|
924
|
-
|
|
925
|
-
/**
|
|
926
|
-
* Cleanup resources (terminate worker pool if active)
|
|
927
|
-
* Call this when you're done with the NavMesh instance.
|
|
928
|
-
*
|
|
929
|
-
* @example
|
|
930
|
-
* ```ts
|
|
931
|
-
* const navmesh = new NavMesh('grid', { workers: true });
|
|
932
|
-
* // ... use navmesh ...
|
|
933
|
-
* navmesh.dispose(); // Cleanup workers
|
|
934
|
-
* ```
|
|
935
|
-
*/
|
|
936
|
-
dispose() {
|
|
937
|
-
if (this.workerPool) {
|
|
938
|
-
this.workerPool.terminate();
|
|
939
|
-
this.workerPool = undefined;
|
|
940
|
-
}
|
|
941
|
-
}
|
|
942
|
-
|
|
943
|
-
/**
|
|
944
|
-
* Get current worker status for debugging/monitoring
|
|
945
|
-
*/
|
|
946
|
-
getWorkerStatus() {
|
|
947
|
-
return {
|
|
948
|
-
workersEnabled: this.options.workers,
|
|
949
|
-
workerPoolActive: !!this.workerPool,
|
|
950
|
-
pendingPaths: this.pendingPaths,
|
|
951
|
-
usingWorkersNow: this.shouldUseWorkers(),
|
|
952
|
-
};
|
|
953
|
-
}
|
|
954
|
-
}
|
|
955
|
-
|
|
956
|
-
/* ---------------------------------- */
|
|
957
|
-
/* A* */
|
|
958
|
-
/* ---------------------------------- */
|
|
959
|
-
|
|
960
|
-
/**
|
|
961
|
-
* A* pathfinding with binary heap and proper open set tracking.
|
|
962
|
-
*
|
|
963
|
-
* Optimizations:
|
|
964
|
-
* - Binary heap: O(log n) operations
|
|
965
|
-
* - Open set tracking: prevents duplicate nodes
|
|
966
|
-
* - Integer cell encoding: eliminates string allocation
|
|
967
|
-
* - Closed set: avoids reprocessing
|
|
968
|
-
*
|
|
969
|
-
* Performance: Handles 10k+ node searches efficiently.
|
|
970
|
-
* Time: O(b^d * log n) where b = branching, d = depth, n = nodes.
|
|
971
|
-
*/
|
|
972
|
-
function aStar(
|
|
973
|
-
start: Vec2,
|
|
974
|
-
goal: Vec2,
|
|
975
|
-
walkable: (x: number, y: number) => boolean
|
|
976
|
-
): Vec2[] {
|
|
977
|
-
const cameFrom = new Map<number, number>();
|
|
978
|
-
const g = new Map<number, number>();
|
|
979
|
-
const closed = new Set<number>();
|
|
980
|
-
const openSet = new Set<number>();
|
|
981
|
-
|
|
982
|
-
const key = (p: Vec2): number => encodeCell(p.x, p.y);
|
|
983
|
-
const h = (a: Vec2, b: Vec2): number =>
|
|
984
|
-
Math.abs(a.x - b.x) + Math.abs(a.y - b.y);
|
|
985
|
-
|
|
986
|
-
const open = new BinaryHeap<number>((nodeKey) => {
|
|
987
|
-
const pos = decodeCell(nodeKey);
|
|
988
|
-
return g.get(nodeKey)! + h(pos, goal);
|
|
989
|
-
});
|
|
990
|
-
|
|
991
|
-
const startKey = key(start);
|
|
992
|
-
g.set(startKey, 0);
|
|
993
|
-
open.push(startKey);
|
|
994
|
-
openSet.add(startKey);
|
|
995
|
-
|
|
996
|
-
while (open.size > 0) {
|
|
997
|
-
const currentKey = open.pop()!;
|
|
998
|
-
openSet.delete(currentKey);
|
|
999
|
-
const current = decodeCell(currentKey);
|
|
1000
|
-
|
|
1001
|
-
if (current.x === goal.x && current.y === goal.y) {
|
|
1002
|
-
return reconstruct(cameFrom, current);
|
|
1003
|
-
}
|
|
1004
|
-
|
|
1005
|
-
closed.add(currentKey);
|
|
1006
|
-
|
|
1007
|
-
for (const d of dirs) {
|
|
1008
|
-
const n = { x: current.x + d.x, y: current.y + d.y };
|
|
1009
|
-
if (!walkable(n.x, n.y)) continue;
|
|
1010
|
-
|
|
1011
|
-
const nk = key(n);
|
|
1012
|
-
if (closed.has(nk)) continue;
|
|
1013
|
-
|
|
1014
|
-
const ng = g.get(currentKey)! + 1;
|
|
1015
|
-
|
|
1016
|
-
if (ng < (g.get(nk) ?? Infinity)) {
|
|
1017
|
-
g.set(nk, ng);
|
|
1018
|
-
cameFrom.set(nk, currentKey);
|
|
1019
|
-
|
|
1020
|
-
if (!openSet.has(nk)) {
|
|
1021
|
-
open.push(nk);
|
|
1022
|
-
openSet.add(nk);
|
|
1023
|
-
}
|
|
1024
|
-
}
|
|
1025
|
-
}
|
|
1026
|
-
}
|
|
1027
|
-
|
|
1028
|
-
return [];
|
|
1029
|
-
}
|
|
1030
|
-
|
|
1031
|
-
/**
|
|
1032
|
-
* Reconstructs path from A* came-from map.
|
|
1033
|
-
*/
|
|
1034
|
-
function reconstruct(
|
|
1035
|
-
cameFrom: Map<number, number>,
|
|
1036
|
-
current: Vec2
|
|
1037
|
-
): Vec2[] {
|
|
1038
|
-
const path = [current];
|
|
1039
|
-
let k = encodeCell(current.x, current.y);
|
|
1040
|
-
|
|
1041
|
-
while (cameFrom.has(k)) {
|
|
1042
|
-
k = cameFrom.get(k)!;
|
|
1043
|
-
path.push(decodeCell(k));
|
|
1044
|
-
}
|
|
1045
|
-
|
|
1046
|
-
return path.reverse();
|
|
1047
|
-
}
|