@woosh/meep-engine 3.1.0 → 3.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (1869) hide show
  1. package/LICENSE +188 -188
  2. package/README.md +152 -152
  3. package/editor/Editor.js +598 -598
  4. package/editor/EditorCameraNavigation.js +205 -205
  5. package/editor/EditorKeyMap.js +182 -182
  6. package/editor/SelectionVisualizer.js +127 -127
  7. package/editor/actions/concrete/ActionInvalidateSampler.js +27 -27
  8. package/editor/actions/concrete/PatchTerrainHeightAction.js +85 -85
  9. package/editor/camera/camera_framing.js +104 -104
  10. package/editor/clipboard.js +184 -184
  11. package/editor/ecs/component/editors/ObservedIntegerEditor.js +22 -22
  12. package/editor/ecs/component/editors/ObservedStringEditor.js +34 -34
  13. package/editor/ecs/component/editors/common/two_way_sync.js +50 -50
  14. package/editor/ecs/component/editors/ecs/GridObstacleEditor.js +19 -19
  15. package/editor/ecs/component/editors/ecs/ParameterLookupTableEditor.js +35 -35
  16. package/editor/ecs/component/editors/ecs/ParameterTrackEditor.js +17 -17
  17. package/editor/ecs/component/editors/ecs/SGMeshEditor.js +24 -24
  18. package/editor/ecs/component/editors/ecs/ShadedGeometryEditor.js +33 -33
  19. package/editor/ecs/component/editors/ecs/Trail3DEditor.js +45 -45
  20. package/editor/ecs/component/editors/ecs/buildGridObstaclePreview.js +19 -19
  21. package/editor/ecs/component/editors/ecs/terrain/TerrainEditor.js +72 -72
  22. package/editor/ecs/component/editors/shade/MeshletGeometryEditor.js +30 -30
  23. package/editor/ecs/component/editors/shade/ShadeImageEditor.js +70 -70
  24. package/editor/ecs/component/editors/shade/ShadeMaterialEditor.js +34 -34
  25. package/editor/ecs/component/editors/shade/ShadeTextureEditor.js +46 -46
  26. package/editor/ecs/component/editors/shade/StandardShadeMaterialEditor.js +24 -24
  27. package/editor/ecs/component/prototypeObjectEditor.js +265 -265
  28. package/editor/ecs/component/registerBasicTypeEditors.js +100 -100
  29. package/editor/ecs/component/registerEngineComponentEditors.js +95 -95
  30. package/editor/entity_world_bounds.js +27 -27
  31. package/editor/particles/lut_ops.js +148 -148
  32. package/editor/particles/particleEditEvents.js +12 -12
  33. package/editor/particles/rebuildParticleEmitter.js +31 -31
  34. package/editor/persistence/EditorLayoutStore.js +71 -71
  35. package/editor/persistence/SceneMetadata.js +52 -52
  36. package/editor/persistence/SceneSlotManager.js +144 -144
  37. package/editor/persistence/buildNewSceneDataset.js +67 -67
  38. package/editor/persistence/files.js +68 -68
  39. package/editor/persistence/scene_import.js +143 -143
  40. package/editor/persistence/scene_serialization.js +79 -79
  41. package/editor/persistence/string_buffer_codec.js +41 -41
  42. package/editor/process/GridDisplayProcess.js +125 -125
  43. package/editor/process/SymbolicDisplayProcess.js +106 -108
  44. package/editor/process/symbolic/CameraSymbolicDisplay.js +146 -146
  45. package/editor/process/symbolic/LightSymbolicDisplay.js +172 -172
  46. package/editor/process/symbolic/PathSymbolicDisplay.js +118 -118
  47. package/editor/process/symbolic/PositionedMarkerSymbolicDisplay.js +79 -79
  48. package/editor/process/symbolic/SoundEmitterSymbolicDisplay.js +57 -57
  49. package/editor/process/symbolic/SymbolicDisplay.js +62 -62
  50. package/editor/process/symbolic/gizmo_draw_wire_box_oriented.js +85 -85
  51. package/editor/process/symbolic/gizmo_draw_wire_circle.js +53 -53
  52. package/editor/prototypeEditorShell.js +156 -156
  53. package/editor/selection/editor_pick.js +130 -130
  54. package/editor/selection/screen_rect_frustum.js +119 -119
  55. package/editor/style/controls.scss +168 -168
  56. package/editor/style/inspector.scss +223 -223
  57. package/editor/templates/entity_templates.js +167 -167
  58. package/editor/tools/GridPaintTool.js +273 -273
  59. package/editor/tools/SelectionTool.js +307 -307
  60. package/editor/tools/TransformToolV2.js +378 -378
  61. package/editor/tools/engine/ToolEngine.js +188 -188
  62. package/editor/tools/paint/TerrainTexturePaintTool.js +250 -250
  63. package/editor/tools/v2/BlenderCameraOrientationGizmo.js +501 -501
  64. package/editor/tools/v2/TransformControls.js +971 -971
  65. package/editor/tools/v2/handle/gizmo_drag_plane.js +141 -141
  66. package/editor/tools/v2/handle/gizmo_draw.js +399 -399
  67. package/editor/tools/v2/handle/gizmo_handles.js +364 -364
  68. package/editor/tools/v2/handle/gizmo_ray_shapes.js +239 -239
  69. package/editor/view/ecs/HierarchicalEntityListView.js +339 -339
  70. package/editor/view/ecs/components/DatGuiController.js +2 -2
  71. package/editor/view/ecs/components/common/AutoCanvasView.js +100 -100
  72. package/editor/view/ecs/components/common/TextController.js +59 -59
  73. package/editor/view/library/MeshLibraryView.js +175 -175
  74. package/editor/view/library/mesh_library_drag.js +63 -63
  75. package/editor/view/library/model_thumbnail_source.js +86 -86
  76. package/editor/view/makeEntityDecorators.js +125 -125
  77. package/editor/view/node-graph/NodeGraphSelection.js +89 -89
  78. package/editor/view/node-graph/NodeView.js +211 -211
  79. package/editor/view/node-graph/actions/ConnectionDeleteAction.js +36 -36
  80. package/editor/view/node-graph/actions/NodesMoveAction.js +41 -41
  81. package/editor/view/node-graph/connection_wire_geometry.js +107 -107
  82. package/editor/view/particles/ColorLutGradientView.js +291 -291
  83. package/editor/view/particles/ScalarLutCurveView.js +279 -279
  84. package/editor/view/prepareMeshLibrary.js +188 -188
  85. package/editor/view/shell/SceneTreePanelView.js +119 -119
  86. package/editor/view/v2/SplitView.js +2 -2
  87. package/package.json +94 -94
  88. package/samples/generation/generators/interactive/mir_generator_place_buff_objects.js +362 -362
  89. package/src/DUPLICATION_AUDIT_2026_08_07/00_core_reuse_surface.md +738 -738
  90. package/src/DUPLICATION_AUDIT_2026_08_07/A_core_math_geom.md +79 -79
  91. package/src/DUPLICATION_AUDIT_2026_08_07/B_core_data.md +93 -93
  92. package/src/DUPLICATION_AUDIT_2026_08_07/C_gfx_render.md +80 -80
  93. package/src/DUPLICATION_AUDIT_2026_08_07/D_gfx_features.md +164 -164
  94. package/src/DUPLICATION_AUDIT_2026_08_07/E_physics.md +69 -69
  95. package/src/DUPLICATION_AUDIT_2026_08_07/F_sound.md +102 -102
  96. package/src/DUPLICATION_AUDIT_2026_08_07/G_net_save.md +129 -129
  97. package/src/DUPLICATION_AUDIT_2026_08_07/H_engine_misc.md +72 -72
  98. package/src/DUPLICATION_AUDIT_2026_08_07/I_generation_view.md +74 -74
  99. package/src/DUPLICATION_AUDIT_2026_08_07/K_crosscutting.md +169 -169
  100. package/src/DUPLICATION_AUDIT_2026_08_07.md +254 -254
  101. package/src/DUPLICATION_AUDIT_REMEDIATION.md +250 -250
  102. package/src/REVIEW_2026_08_06/adv-core.md +511 -511
  103. package/src/REVIEW_2026_08_06/adv-gfx.md +480 -480
  104. package/src/REVIEW_2026_08_06/adv-view.md +446 -446
  105. package/src/REVIEW_2026_08_06/api-core.md +296 -296
  106. package/src/REVIEW_2026_08_06/api-engine.md +195 -195
  107. package/src/REVIEW_2026_08_06/bvh-phys-sound-net.md +507 -507
  108. package/src/REVIEW_2026_08_06/core-binary.md +348 -348
  109. package/src/REVIEW_2026_08_06/core-collection.md +376 -376
  110. package/src/REVIEW_2026_08_06/core-math.md +221 -221
  111. package/src/REVIEW_2026_08_06/core-process.md +266 -266
  112. package/src/REVIEW_2026_08_06/core-science.md +223 -223
  113. package/src/REVIEW_2026_08_06/engine-ai-nav.md +279 -279
  114. package/src/REVIEW_2026_08_06/engine-animation.md +167 -167
  115. package/src/REVIEW_2026_08_06/engine-asset.md +166 -166
  116. package/src/REVIEW_2026_08_06/engine-ecs.md +920 -920
  117. package/src/REVIEW_2026_08_06/engine-input.md +197 -197
  118. package/src/REVIEW_2026_08_06/engine-misc.md +248 -248
  119. package/src/REVIEW_2026_08_06/engine-terrain.md +52 -52
  120. package/src/REVIEW_2026_08_06/generation.md +176 -176
  121. package/src/REVIEW_2026_08_06/geom-2d.md +434 -434
  122. package/src/REVIEW_2026_08_06/geom-3d.md +117 -117
  123. package/src/REVIEW_2026_08_06/gfx-gi.md +143 -143
  124. package/src/REVIEW_2026_08_06/gfx-render.md +740 -740
  125. package/src/REVIEW_2026_08_06/gfx-texture.md +690 -690
  126. package/src/REVIEW_2026_08_06/prior-criticals.md +49 -49
  127. package/src/REVIEW_2026_08_06/prior-highs.md +138 -138
  128. package/src/REVIEW_2026_08_06/test-gaps.md +364 -364
  129. package/src/REVIEW_2026_08_06/view.md +667 -667
  130. package/src/REVIEW_2026_08_06.md +610 -610
  131. package/src/avif/codec/dec/avif_dec.cpp +233 -233
  132. package/src/avif/codec/dec/avif_dec.js +16 -16
  133. package/src/avif/encoder_worker.js +5 -18
  134. package/src/core/binary/BitImage2.js +246 -246
  135. package/src/core/binary/align_32.js +21 -21
  136. package/src/core/binary/data_view/DataType2DataViewReaders.js +23 -23
  137. package/src/core/binary/data_view/DataType2DataViewWriters.js +25 -25
  138. package/src/core/binary/hash/XXH128Hash.js +55 -55
  139. package/src/core/binary/hash/XXH3_SECRET.js +25 -25
  140. package/src/core/binary/hash/xxh3_128.js +341 -341
  141. package/src/core/binary/hash/xxh3_64.js +241 -241
  142. package/src/core/binary/hash/xxh3_common.js +476 -476
  143. package/src/core/binary/lz4/LZ4_MAX_INPUT_SIZE.js +9 -9
  144. package/src/core/binary/lz4/lz4_compress_block.js +385 -385
  145. package/src/core/binary/lz4/lz4_compress_bound.js +25 -25
  146. package/src/core/binary/lz4/lz4_decompress_block.js +139 -139
  147. package/src/core/binary/lz4/lz4_decompress_bound.js +30 -30
  148. package/src/core/binary/meshopt/MESHOPT_GROUP_SIZE.js +18 -18
  149. package/src/core/binary/meshopt/MeshoptCompressionFilter.js +30 -30
  150. package/src/core/binary/meshopt/MeshoptCompressionMode.js +21 -21
  151. package/src/core/binary/meshopt/__meshopt_test_streams.js +237 -237
  152. package/src/core/binary/meshopt/meshopt_decode.js +73 -73
  153. package/src/core/binary/meshopt/meshopt_decode_byte_group.js +67 -67
  154. package/src/core/binary/meshopt/meshopt_decode_data_block.js +59 -59
  155. package/src/core/binary/meshopt/meshopt_decode_filter_color.js +69 -69
  156. package/src/core/binary/meshopt/meshopt_decode_filter_exponential.js +43 -43
  157. package/src/core/binary/meshopt/meshopt_decode_filter_octahedral.js +60 -60
  158. package/src/core/binary/meshopt/meshopt_decode_filter_quaternion.js +71 -71
  159. package/src/core/binary/meshopt/meshopt_decode_index_buffer.js +294 -294
  160. package/src/core/binary/meshopt/meshopt_decode_index_sequence.js +115 -115
  161. package/src/core/binary/meshopt/meshopt_decode_vertex_buffer.js +381 -381
  162. package/src/core/binary/meshopt/meshopt_read_uint_var.js +59 -59
  163. package/src/core/binary/reinterpret_float32_as_uint32.js +13 -13
  164. package/src/core/binary/reinterpret_uint32_as_float32.js +14 -14
  165. package/src/core/binary/type/DataType2TypedArrayConstructorMapping.js +35 -35
  166. package/src/core/bvh2/bvh3/ebvh_geometry_query_any_triangle_ray.js +149 -149
  167. package/src/core/bvh2/bvh3/ebvh_sort_for_traversal_depth_first.js +96 -96
  168. package/src/core/bvh2/bvh3/query/bvh_query_depth_range_in_frustum.js +101 -101
  169. package/src/core/bvh2/bvh3/query/bvh_query_leaves_generic.js +77 -77
  170. package/src/core/bvh2/bvh3/query/bvh_query_leaves_ray.js +90 -90
  171. package/src/core/bvh2/bvh3/query/bvh_query_leaves_ray_segment.js +94 -94
  172. package/src/core/bvh2/bvh3/query/bvh_query_user_data_generic.js +86 -86
  173. package/src/core/bvh2/bvh3/query/bvh_query_user_data_intersects_capsule.js +101 -101
  174. package/src/core/bvh2/bvh3/query/bvh_query_user_data_intersects_sphere.js +92 -92
  175. package/src/core/bvh2/bvh3/query/bvh_query_user_data_ray.js +96 -96
  176. package/src/core/bvh2/bvh3/query/bvh_query_user_data_ray_segment.js +100 -100
  177. package/src/core/bvh2/visual/BVHGeometry.js +131 -131
  178. package/src/core/bvh8/BVH8.js +436 -436
  179. package/src/core/bvh8/aabb3_round_to_float32.js +22 -22
  180. package/src/core/bvh8/build/BVH8Converter.js +588 -588
  181. package/src/core/bvh8/build/TriangleCluster.js +122 -122
  182. package/src/core/bvh8/build/aabb3_compute_merge_cost.js +28 -28
  183. package/src/core/bvh8/build/aabb3_from_triangle_by_index.js +17 -17
  184. package/src/core/bvh8/build/bvh8_build_for_geometry.js +303 -303
  185. package/src/core/bvh8/build/bvh8_from_proxy.js +256 -256
  186. package/src/core/bvh8/build/byte.js +9 -9
  187. package/src/core/bvh8/build/encode_bounds_e.js +11 -11
  188. package/src/core/bvh8/bvh8_convert_to_dot.js +132 -132
  189. package/src/core/bvh8/bvh8_count_primitives.js +98 -98
  190. package/src/core/bvh8/bvh8_geometry_validate.js +138 -138
  191. package/src/core/bvh8/bvh8_geometry_validate_indirect.js +166 -166
  192. package/src/core/bvh8/bvh8_get_node_bounds.js +34 -34
  193. package/src/core/bvh8/bvh8_get_node_child_bounds.js +52 -52
  194. package/src/core/bvh8/bvh8_node_child_surface_area.js +18 -18
  195. package/src/core/bvh8/bvh8_node_count_triangles.js +27 -27
  196. package/src/core/bvh8/bvh8_quality.js +72 -72
  197. package/src/core/bvh8/bvh8_validate_structure.js +87 -87
  198. package/src/core/clipboard/obtainClipBoard.js +29 -29
  199. package/src/core/clipboard/safeClipboardReadText.js +55 -55
  200. package/src/core/clipboard/safeClipboardWriteText.js +23 -23
  201. package/src/core/collection/array/typed/typed_array_value_denormalize.js +52 -52
  202. package/src/core/collection/array/typed/typed_array_value_normalize.js +51 -51
  203. package/src/core/collection/union-find/union_find.js +79 -79
  204. package/src/core/color/PQ/PQ_constants.d.ts +43 -0
  205. package/src/core/color/PQ/PQ_constants.d.ts.map +1 -0
  206. package/src/core/color/PQ/PQ_constants.js +50 -0
  207. package/src/core/color/PQ/PQ_to_linear.d.ts +24 -0
  208. package/src/core/color/PQ/PQ_to_linear.d.ts.map +1 -0
  209. package/src/core/color/PQ/PQ_to_linear.js +35 -0
  210. package/src/core/color/PQ/linear_to_PQ.d.ts +27 -0
  211. package/src/core/color/PQ/linear_to_PQ.d.ts.map +1 -0
  212. package/src/core/color/PQ/linear_to_PQ.js +32 -0
  213. package/src/core/color/YCoCg/rgb_to_ycocg.js +20 -20
  214. package/src/core/color/YCoCg/ycocg_to_rgb.js +17 -17
  215. package/src/core/color/colormap/MAGMA_LUT.js +26 -26
  216. package/src/core/color/illuminant/D65_TRISTIMULUS_XYZ.js +19 -19
  217. package/src/core/color/lab/lab_apply_hunt_adjustment.js +27 -27
  218. package/src/core/color/lab/lab_distance_hyab.js +23 -23
  219. package/src/core/color/lab/xyz_to_lab.js +62 -62
  220. package/src/core/color/operations/color_darken.js +24 -24
  221. package/src/core/color/operations/color_desaturate.js +24 -24
  222. package/src/core/color/operations/color_lighten.js +24 -24
  223. package/src/core/color/operations/color_saturate.js +24 -24
  224. package/src/core/color/operations/color_scale_okhsv_channel.js +45 -45
  225. package/src/core/color/tonemap/tonemap_aces.js +41 -41
  226. package/src/core/color/ycxcz/xyz_to_ycxcz.js +28 -28
  227. package/src/core/color/ycxcz/ycxcz_to_xyz.js +19 -19
  228. package/src/core/dom/isImageBitmap.js +12 -12
  229. package/src/core/function/frameThrottle.js +23 -23
  230. package/src/core/geom/2d/aabb/aabb2_array_combine.js +7 -7
  231. package/src/core/geom/2d/aabb/aabb2_compute_area.js +7 -7
  232. package/src/core/geom/2d/aabb/aabb2_compute_center_from_multiple.js +7 -7
  233. package/src/core/geom/2d/aabb/aabb2_compute_overlap.js +7 -7
  234. package/src/core/geom/2d/aabb/aabb2_from_v2_array.js +7 -7
  235. package/src/core/geom/2d/aabb/aabb2_intersects_point.js +7 -7
  236. package/src/core/geom/2d/aabb/aabb2_overlap_exists.js +7 -7
  237. package/src/core/geom/2d/circle/circle_compute_circle_intersection.js +7 -7
  238. package/src/core/geom/2d/circle/circle_compute_circle_penetration.js +7 -7
  239. package/src/core/geom/2d/circle/circle_intersects_point.js +7 -7
  240. package/src/core/geom/2d/compute_polygon_area_2d.js +7 -7
  241. package/src/core/geom/2d/convex-hull/fixed_convex_hull_humus.js +7 -7
  242. package/src/core/geom/2d/convex-hull/fixed_convex_hull_relaxation.js +7 -7
  243. package/src/core/geom/2d/convex-hull/orientation3_array.js +7 -7
  244. package/src/core/geom/2d/intersect_ray_2d.js +7 -7
  245. package/src/core/geom/2d/line/line2_distance_to_point_sqr.js +7 -7
  246. package/src/core/geom/2d/line/line2_segment_intersection_fraction_2d.js +44 -44
  247. package/src/core/geom/2d/line/line_segment_compute_line_segment_intersection_2d.js +7 -7
  248. package/src/core/geom/2d/line/line_segment_compute_line_segment_intersection_array_2d.js +7 -7
  249. package/src/core/geom/2d/line/line_segment_compute_line_segment_intersection_vectors_2d.js +7 -7
  250. package/src/core/geom/2d/line/line_segment_compute_line_segment_intersection_vectors_array_2d.js +7 -7
  251. package/src/core/geom/2d/line/line_segment_intersection_fraction_2d.js +7 -7
  252. package/src/core/geom/2d/line/line_segment_line_segment_intersection_exists_2d.js +7 -7
  253. package/src/core/geom/2d/line/segment2_intersects_segment2_2d.js +25 -25
  254. package/src/core/geom/2d/line/segment2_segment_compute_intersection_array_2d.js +46 -46
  255. package/src/core/geom/2d/line/segment2_segment_intersection_fractions.js +52 -52
  256. package/src/core/geom/2d/oriented-box/min_area_rect_2d.js +69 -69
  257. package/src/core/geom/2d/polygon/TRIANGULATION_DESIGN.md +433 -433
  258. package/src/core/geom/2d/polygon/polygon2_clip_axis_halfplane.js +51 -51
  259. package/src/core/geom/2d/polygon/polygon2_clip_halfplane.js +50 -50
  260. package/src/core/geom/2d/polygon/polygon2_is_counter_clockwise.js +84 -84
  261. package/src/core/geom/2d/polygon/polygon2_signed_area.js +52 -52
  262. package/src/core/geom/2d/polygon/polygon2_triangulate.corpus.js +86 -86
  263. package/src/core/geom/2d/polygon/polygon2_triangulate.js +1280 -1280
  264. package/src/core/geom/2d/polygon/polygon2_triangulation_deviation.js +77 -77
  265. package/src/core/geom/2d/quad-tree/qt_collect_by_circle.js +39 -39
  266. package/src/core/geom/2d/quad-tree/qt_match_data_by_circle.js +101 -101
  267. package/src/core/geom/2d/quad-tree/qt_query_data_nearest_to_point.js +7 -7
  268. package/src/core/geom/2d/rectangle_to_aabb.js +7 -7
  269. package/src/core/geom/2d/spline_bezier2_2d.js +7 -7
  270. package/src/core/geom/2d/triangle/tri2_rasterize_conservative.js +99 -99
  271. package/src/core/geom/2d/triangle/tri2_signed_area.js +35 -35
  272. package/src/core/geom/2d/triangle2d_compute_area.js +7 -7
  273. package/src/core/geom/2d/triangle2d_contains_point.js +7 -7
  274. package/src/core/geom/2d/triangle2d_get_barycentric.js +7 -7
  275. package/src/core/geom/2d/v2_morton_encode.js +23 -23
  276. package/src/core/geom/3d/aabb/aabb3_array_intersects_frustum_degree.js +7 -7
  277. package/src/core/geom/3d/aabb/aabb3_box_surface_area_2.js +7 -7
  278. package/src/core/geom/3d/aabb/aabb3_build_frustum.js +5 -5
  279. package/src/core/geom/3d/aabb/aabb3_closest_point_on_surface.js +57 -57
  280. package/src/core/geom/3d/aabb/aabb3_combined_surface_area.js +7 -7
  281. package/src/core/geom/3d/aabb/aabb3_compute_half_surface_area.js +7 -7
  282. package/src/core/geom/3d/aabb/aabb3_compute_projected_area.js +7 -7
  283. package/src/core/geom/3d/aabb/aabb3_compute_surface_area.js +7 -7
  284. package/src/core/geom/3d/aabb/aabb3_detailed_volume_intersection.js +7 -7
  285. package/src/core/geom/3d/aabb/aabb3_estimate_projected_area2.js +7 -7
  286. package/src/core/geom/3d/aabb/aabb3_from_threejs_geometry.js +7 -7
  287. package/src/core/geom/3d/aabb/aabb3_from_triangle_group.js +46 -46
  288. package/src/core/geom/3d/aabb/aabb3_intersects_frustum_degree.js +7 -7
  289. package/src/core/geom/3d/aabb/aabb3_intersects_line_segment.js +7 -7
  290. package/src/core/geom/3d/aabb/aabb3_near_distance_to_intersection_ray_segment.js +7 -7
  291. package/src/core/geom/3d/aabb/aabb3_nearest_point_on_surface.js +7 -7
  292. package/src/core/geom/3d/aabb/aabb3_ray_segment_entry_distance.js +95 -95
  293. package/src/core/geom/3d/aabb/aabb3_raycast.js +7 -7
  294. package/src/core/geom/3d/aabb/aabb3_transform_oriented.js +93 -93
  295. package/src/core/geom/3d/aabb/aabb3_transform_oriented_inverse.js +82 -82
  296. package/src/core/geom/3d/aabb/compute_aabb_from_points.js +7 -7
  297. package/src/core/geom/3d/aabb/compute_triangle_group_aabb3.js +7 -7
  298. package/src/core/geom/3d/atlas/ATLAS_PORT_PLAN.md +486 -486
  299. package/src/core/geom/3d/atlas/BFF_PLAN.md +174 -174
  300. package/src/core/geom/3d/atlas/Chart.js +77 -77
  301. package/src/core/geom/3d/atlas/REVIEW_02.md +392 -392
  302. package/src/core/geom/3d/atlas/atlas_bench_lib.js +315 -315
  303. package/src/core/geom/3d/atlas/atlas_chart_type.js +16 -16
  304. package/src/core/geom/3d/atlas/atlas_compute_charts.js +188 -188
  305. package/src/core/geom/3d/atlas/atlas_extract_chart.js +107 -107
  306. package/src/core/geom/3d/atlas/atlas_generate.js +50 -50
  307. package/src/core/geom/3d/atlas/atlas_test_fixtures.js +266 -266
  308. package/src/core/geom/3d/atlas/atlas_write_chart_uvs.js +36 -36
  309. package/src/core/geom/3d/atlas/data/atlas_build_edge_dihedrals.js +62 -62
  310. package/src/core/geom/3d/atlas/data/atlas_build_edge_lengths.js +33 -33
  311. package/src/core/geom/3d/atlas/data/atlas_build_face_areas.js +50 -50
  312. package/src/core/geom/3d/atlas/io/atlas_mesh_weld_colocals.js +39 -39
  313. package/src/core/geom/3d/atlas/io/bt_loop_uv.js +35 -35
  314. package/src/core/geom/3d/atlas/pack/ChartPlacement.js +46 -46
  315. package/src/core/geom/3d/atlas/param/atlas_chart_parameterize_bff.js +269 -269
  316. package/src/core/geom/3d/atlas/param/atlas_chart_parameterize_lscm.js +209 -209
  317. package/src/core/geom/3d/atlas/param/atlas_chart_parameterize_ortho.js +38 -38
  318. package/src/core/geom/3d/atlas/param/atlas_chart_stretch_metric.js +86 -86
  319. package/src/core/geom/3d/atlas/param/atlas_piecewise_resegment.js +183 -183
  320. package/src/core/geom/3d/atlas/param/fit_plane_basis.js +66 -66
  321. package/src/core/geom/3d/atlas/segment/RegionGraph.js +40 -40
  322. package/src/core/geom/3d/atlas/segment/atlas_build_region_graph.js +136 -136
  323. package/src/core/geom/3d/atlas/segment/atlas_chart_roundness_delta.js +30 -30
  324. package/src/core/geom/3d/atlas/segment/atlas_compute_planar_regions.js +77 -77
  325. package/src/core/geom/3d/atlas/segment/atlas_merge_charts.js +116 -116
  326. package/src/core/geom/3d/atlas/segment/atlas_segment_charts.js +271 -271
  327. package/src/core/geom/3d/atlas/segment/atlas_segment_charts_atomic.js +191 -191
  328. package/src/core/geom/3d/box/box3_projected_half_extent.js +35 -35
  329. package/src/core/geom/3d/capsule/capsule_intersects_aabb3_closed.js +67 -67
  330. package/src/core/geom/3d/capsule/capsule_intersects_aabb3_iterative.js +137 -137
  331. package/src/core/geom/3d/compute_circle_bounding_box.js +7 -7
  332. package/src/core/geom/3d/cone/computeConeBoundingBox.js +7 -7
  333. package/src/core/geom/3d/cone/computeConePlaneSide.js +7 -7
  334. package/src/core/geom/3d/cone/compute_bounding_cone_of_2_cones.js +7 -7
  335. package/src/core/geom/3d/cylinder/cylinder3_raycast.d.ts +38 -0
  336. package/src/core/geom/3d/cylinder/cylinder3_raycast.d.ts.map +1 -0
  337. package/src/core/geom/3d/cylinder/cylinder3_raycast.js +105 -0
  338. package/src/core/geom/3d/equirectangular/equirectangular_direction_to_uv.js +18 -18
  339. package/src/core/geom/3d/equirectangular/equirectangular_uv_to_direction.js +24 -24
  340. package/src/core/geom/3d/frustum/clipping_volume_matrix4_project.js +7 -7
  341. package/src/core/geom/3d/frustum/frustum3_computeNearestPointToPoint.js +7 -7
  342. package/src/core/geom/3d/frustum/frustum3_compute_closest_point.js +117 -117
  343. package/src/core/geom/3d/frustum/frustum_matrix4_project.js +7 -7
  344. package/src/core/geom/3d/frustum/hexahedron_from_aabb.js +7 -7
  345. package/src/core/geom/3d/frustum/read_frustum_corner.js +17 -17
  346. package/src/core/geom/3d/frustum/read_three_planes_to_array.js +7 -7
  347. package/src/core/geom/3d/frustum/slice_frustum_linear_to_points.js +7 -7
  348. package/src/core/geom/3d/gjk/GJK_REVIEW_NOTES.md +146 -146
  349. package/src/core/geom/3d/gjk/gjk.js +430 -430
  350. package/src/core/geom/3d/gjk/gjk_epa_penetration.js +520 -520
  351. package/src/core/geom/3d/gjk/minkowski_support.js +71 -71
  352. package/src/core/geom/3d/gjk/mpr.js +368 -368
  353. package/src/core/geom/3d/line/line3_closest_points_segment_segment.js +7 -7
  354. package/src/core/geom/3d/line/line3_compute_nearest_point_to_point.js +7 -7
  355. package/src/core/geom/3d/line/line3_compute_point_segment_point_distance_unfolded.js +64 -64
  356. package/src/core/geom/3d/line/line3_compute_segment_closest_point_to_aabb3_t.js +153 -153
  357. package/src/core/geom/3d/line/line3_compute_segment_nearest_point_to_aabb3_t.js +7 -7
  358. package/src/core/geom/3d/line/line3_compute_segment_nearest_point_to_point_t.js +7 -7
  359. package/src/core/geom/3d/line/line3_compute_segment_segment_closest_points.js +88 -88
  360. package/src/core/geom/3d/line/line3_segment_segment_closest_point_pair.js +71 -71
  361. package/src/core/geom/3d/line/segment3_point_distance_to_segments.js +25 -25
  362. package/src/core/geom/3d/mat4/allocate_m4.js +7 -7
  363. package/src/core/geom/3d/mat4/apply_mat4_transform_to_direction_v3_array.js +7 -7
  364. package/src/core/geom/3d/mat4/apply_mat4_transform_to_v3_array.js +7 -7
  365. package/src/core/geom/3d/mat4/compose_matrix4_array.js +7 -7
  366. package/src/core/geom/3d/mat4/decompose_matrix_4_array.js +7 -7
  367. package/src/core/geom/3d/mat4/eulerAnglesFromMatrix.js +7 -7
  368. package/src/core/geom/3d/mat4/m4_compose.js +22 -22
  369. package/src/core/geom/3d/mat4/m4_compute_normal_matrix3.js +7 -7
  370. package/src/core/geom/3d/mat4/m4_decompose.js +64 -64
  371. package/src/core/geom/3d/mat4/m4_decompose_array.js +157 -157
  372. package/src/core/geom/3d/mat4/m4_from_rotation_translation_scale.js +32 -32
  373. package/src/core/geom/3d/mat4/m4_from_rotation_translation_scale_scalar.js +67 -67
  374. package/src/core/geom/3d/mat4/m4_linear_matrix3.js +29 -29
  375. package/src/core/geom/3d/mat4/m4_look_at.js +96 -96
  376. package/src/core/geom/3d/mat4/m4_make_rotation_x.js +47 -47
  377. package/src/core/geom/3d/mat4/m4_make_rotation_y.js +47 -47
  378. package/src/core/geom/3d/mat4/m4_make_rotation_z.js +47 -47
  379. package/src/core/geom/3d/mat4/m4_normal_matrix3.js +39 -39
  380. package/src/core/geom/3d/mat4/m4_perspective.js +66 -66
  381. package/src/core/geom/3d/mat4/m4_transpose.js +48 -48
  382. package/src/core/geom/3d/morton/mortonEncode_LUT.js +7 -7
  383. package/src/core/geom/3d/morton/mortonEncode_magicbits.js +7 -7
  384. package/src/core/geom/3d/normal/hemioct/encode_unit3_hemioct.js +5 -5
  385. package/src/core/geom/3d/normal/hemioct/encode_unit_to_hemioct.js +28 -28
  386. package/src/core/geom/3d/normal/lambert_azimuth/decode_azimuth_to_unit.js +30 -30
  387. package/src/core/geom/3d/normal/lambert_azimuth/encode_unit_to_azimuth.js +19 -19
  388. package/src/core/geom/3d/normal/spherical/decode_spheremap_to_unit.js +38 -38
  389. package/src/core/geom/3d/normal/spherical/encode_unit_to_spheremap.js +30 -30
  390. package/src/core/geom/3d/plane/is_point_within_planes.js +7 -7
  391. package/src/core/geom/3d/plane/orient3d_fast_point.js +47 -47
  392. package/src/core/geom/3d/plane/plane3_compute_convex_3_plane_intersection.js +7 -7
  393. package/src/core/geom/3d/plane/plane3_compute_plane_intersection.js +7 -7
  394. package/src/core/geom/3d/plane/plane3_intersect_plane.js +7 -7
  395. package/src/core/geom/3d/plane/plane3_three_compute_convex_3_plane_intersection.js +7 -7
  396. package/src/core/geom/3d/point/point3_raycast.d.ts +33 -0
  397. package/src/core/geom/3d/point/point3_raycast.d.ts.map +1 -0
  398. package/src/core/geom/3d/point/point3_raycast.js +47 -0
  399. package/src/core/geom/3d/polygon/polygon3_compute_normal.js +60 -60
  400. package/src/core/geom/3d/polygon/polygon3_triangulate.js +94 -94
  401. package/src/core/geom/3d/polyhedron/convex_polyhedron3_raycast.d.ts +40 -0
  402. package/src/core/geom/3d/polyhedron/convex_polyhedron3_raycast.d.ts.map +1 -0
  403. package/src/core/geom/3d/polyhedron/convex_polyhedron3_raycast.js +94 -0
  404. package/src/core/geom/3d/quadric/QUADRIC3_SIZE.js +14 -14
  405. package/src/core/geom/3d/quadric/Quadric3.js +282 -282
  406. package/src/core/geom/3d/quadric/quadric3_add.js +33 -33
  407. package/src/core/geom/3d/quadric/quadric3_clear.js +25 -25
  408. package/src/core/geom/3d/quadric/quadric3_copy.js +27 -27
  409. package/src/core/geom/3d/quadric/quadric3_distance.js +36 -36
  410. package/src/core/geom/3d/quadric/quadric3_evaluate.js +25 -25
  411. package/src/core/geom/3d/quadric/quadric3_optimize.js +45 -45
  412. package/src/core/geom/3d/quadric/quadric3_scale.js +32 -32
  413. package/src/core/geom/3d/quadric/quadric3_set_from_plane_through_point.js +30 -30
  414. package/src/core/geom/3d/quadric/quadric3_set_from_vector4.js +35 -35
  415. package/src/core/geom/3d/quadric/quadric3_sub.js +36 -36
  416. package/src/core/geom/3d/quadric/quadric3_to_tensor_m3.js +28 -28
  417. package/src/core/geom/3d/quadric/quadric3_to_tensor_m3_inverse.js +56 -56
  418. package/src/core/geom/3d/quadric/quadric3_to_vector3.js +19 -19
  419. package/src/core/geom/3d/quaternion/quat3_createFromAxisAngle.js +7 -7
  420. package/src/core/geom/3d/quaternion/quat3_integrate.js +62 -62
  421. package/src/core/geom/3d/quaternion/quat3_multiply.js +31 -31
  422. package/src/core/geom/3d/quaternion/quat3_nlerp.js +72 -72
  423. package/src/core/geom/3d/quaternion/quat3_to_matrix3.js +69 -69
  424. package/src/core/geom/3d/quaternion/quat_decode_from_uint32.js +7 -7
  425. package/src/core/geom/3d/quaternion/quat_encode_to_uint32.js +7 -7
  426. package/src/core/geom/3d/ray/ray3_array_compose.js +7 -7
  427. package/src/core/geom/3d/ray/ray3_compute_nearest_point_to_point.js +7 -7
  428. package/src/core/geom/3d/ray/ray3_distance_to_point.js +7 -7
  429. package/src/core/geom/3d/ray/ray3_shift_origin_along_direction.js +7 -7
  430. package/src/core/geom/3d/shape/ConvexHullShape3D.d.ts +17 -0
  431. package/src/core/geom/3d/shape/ConvexHullShape3D.d.ts.map +1 -1
  432. package/src/core/geom/3d/shape/ConvexHullShape3D.js +43 -0
  433. package/src/core/geom/3d/shape/CylinderShape3D.d.ts +1 -0
  434. package/src/core/geom/3d/shape/CylinderShape3D.d.ts.map +1 -1
  435. package/src/core/geom/3d/shape/CylinderShape3D.js +21 -0
  436. package/src/core/geom/3d/shape/PointShape3D.d.ts +7 -0
  437. package/src/core/geom/3d/shape/PointShape3D.d.ts.map +1 -1
  438. package/src/core/geom/3d/shape/PointShape3D.js +88 -63
  439. package/src/core/geom/3d/shape/PosedShape3D.d.ts +6 -0
  440. package/src/core/geom/3d/shape/PosedShape3D.d.ts.map +1 -1
  441. package/src/core/geom/3d/shape/PosedShape3D.js +27 -0
  442. package/src/core/geom/3d/shape/TransformedShape3D.d.ts +10 -0
  443. package/src/core/geom/3d/shape/TransformedShape3D.d.ts.map +1 -1
  444. package/src/core/geom/3d/shape/TransformedShape3D.js +13 -0
  445. package/src/core/geom/3d/shape/Triangle3D.d.ts +1 -0
  446. package/src/core/geom/3d/shape/Triangle3D.d.ts.map +1 -1
  447. package/src/core/geom/3d/shape/Triangle3D.js +26 -0
  448. package/src/core/geom/3d/shape/UnionShape3D.d.ts +6 -0
  449. package/src/core/geom/3d/shape/UnionShape3D.d.ts.map +1 -1
  450. package/src/core/geom/3d/shape/UnionShape3D.js +7 -0
  451. package/src/core/geom/3d/shape/UnitCubeShape3D.js +45 -45
  452. package/src/core/geom/3d/shape/UnitSphereShape3D.js +44 -44
  453. package/src/core/geom/3d/shape/shape3_mesh_from_geometry.js +64 -64
  454. package/src/core/geom/3d/shape/shape_mesh_from_geometry.js +7 -7
  455. package/src/core/geom/3d/shape/util/compute_signed_distance_gradient_by_sampling.js +51 -51
  456. package/src/core/geom/3d/shape/util/shape3d_voxelize_to_grid.js +7 -7
  457. package/src/core/geom/3d/sphere/compute_bounding_sphere_of_2_spheres.js +7 -7
  458. package/src/core/geom/3d/sphere/harmonics/sh3_convolve_with_zh.js +53 -53
  459. package/src/core/geom/3d/sphere/harmonics/sh3_ggx_zh.js +30 -30
  460. package/src/core/geom/3d/sphere/harmonics/sh3_rotate_bl.js +7 -7
  461. package/src/core/geom/3d/sphere/harmonics/sh_compute_K.js +7 -7
  462. package/src/core/geom/3d/sphere/sphere_array_intersects_point.js +7 -7
  463. package/src/core/geom/3d/sphere/sphere_array_intersects_ray.js +7 -7
  464. package/src/core/geom/3d/sphere/sphere_intersects_point.js +7 -7
  465. package/src/core/geom/3d/sphere/sphere_matrix4_transform.js +5 -5
  466. package/src/core/geom/3d/sphere/sphere_projected_sphere_radius_sqr.js +7 -7
  467. package/src/core/geom/3d/sphere/sphere_radius_sqr_from_v3_array_transformed.js +7 -7
  468. package/src/core/geom/3d/sphere/spherical_from_cartesian.js +7 -7
  469. package/src/core/geom/3d/tetrahedra/compute_tetrahedral_mesh_from_surface.js +174 -174
  470. package/src/core/geom/3d/tetrahedra/compute_tetrahedron_quality.js +7 -7
  471. package/src/core/geom/3d/tetrahedra/compute_tetrahedron_volume.js +7 -7
  472. package/src/core/geom/3d/tetrahedra/delaunay/fill_in_a_cavity.js +7 -7
  473. package/src/core/geom/3d/tetrahedra/graphics/build_tetrahedral_mesh_geometry.js +39 -39
  474. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_build_vertex_to_tets_map.js +48 -48
  475. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_carve_outside_surface.js +173 -173
  476. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_compute_tet_quality.js +22 -22
  477. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_find_tets_around_edge.js +146 -146
  478. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_flip_23.js +232 -232
  479. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_flip_32.js +255 -255
  480. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_improve_quality.js +387 -387
  481. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_smooth_vertex.js +140 -140
  482. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_tet_get_neighbours.js +7 -7
  483. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_vertex_is_boundary.js +97 -97
  484. package/src/core/geom/3d/tetrahedra/tetrahedron_compute_quality.js +66 -66
  485. package/src/core/geom/3d/tetrahedra/triangle/triangle_decode_depth.js +59 -59
  486. package/src/core/geom/3d/tetrahedra/triangle/triangle_rasterized_depth_sample_index.js +54 -54
  487. package/src/core/geom/3d/tetrahedra/validate_neighbour.js +70 -70
  488. package/src/core/geom/3d/topology/struct/binary/BinaryDataBlock.js +314 -314
  489. package/src/core/geom/3d/topology/struct/binary/BinaryDataLayer.js +364 -364
  490. package/src/core/geom/3d/topology/struct/binary/BinaryElementPool.js +567 -567
  491. package/src/core/geom/3d/topology/struct/binary/BinaryTopology.js +1085 -1085
  492. package/src/core/geom/3d/topology/struct/binary/io/bt_boundary_loop.js +73 -73
  493. package/src/core/geom/3d/topology/struct/binary/io/bt_loop_kill.js +35 -35
  494. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_append.js +119 -119
  495. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_close_boundary_holes.js +84 -84
  496. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compact.js +172 -172
  497. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compute_face_normals.js +56 -56
  498. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compute_vertex_quadratics.js +7 -7
  499. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compute_vertex_quadrics.js +227 -227
  500. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_face_decouple.js +161 -161
  501. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_face_island_erode.js +490 -490
  502. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_fill_small_holes.js +120 -120
  503. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_from_indexed_geometry.js +120 -120
  504. package/src/core/geom/3d/topology/struct/binary/io/edge/bt_edge_collapse.js +52 -52
  505. package/src/core/geom/3d/topology/struct/binary/io/edge/bt_edge_create.js +115 -115
  506. package/src/core/geom/3d/topology/struct/binary/io/edge/bt_edge_flip.js +142 -142
  507. package/src/core/geom/3d/topology/struct/binary/io/edge/bt_edge_get_or_create.js +21 -21
  508. package/src/core/geom/3d/topology/struct/binary/io/edge/bt_edge_kill_parallels.js +92 -92
  509. package/src/core/geom/3d/topology/struct/binary/io/edge/bt_edge_split.js +179 -179
  510. package/src/core/geom/3d/topology/struct/binary/io/edge/bt_edge_swap_vertex_slots.js +28 -28
  511. package/src/core/geom/3d/topology/struct/binary/io/edge/bt_kill_only_edge.js +8 -8
  512. package/src/core/geom/3d/topology/struct/binary/io/edge/bt_mesh_fuse_duplicate_edges.js +83 -83
  513. package/src/core/geom/3d/topology/struct/binary/io/edge/bt_mesh_kill_short_edges.js +89 -89
  514. package/src/core/geom/3d/topology/struct/binary/io/face/bt_face_kill.js +25 -25
  515. package/src/core/geom/3d/topology/struct/binary/io/face/bt_face_poke.js +127 -127
  516. package/src/core/geom/3d/topology/struct/binary/io/face/bt_face_triangulate.js +268 -268
  517. package/src/core/geom/3d/topology/struct/binary/io/face/bt_kill_only_face.js +8 -8
  518. package/src/core/geom/3d/topology/struct/binary/io/face/bt_mesh_kill_degenerate_faces.js +72 -72
  519. package/src/core/geom/3d/topology/struct/binary/io/vertex/bt_kill_only_vert.js +7 -7
  520. package/src/core/geom/3d/topology/struct/binary/io/vertex/bt_merge_verts_by_distance.js +7 -7
  521. package/src/core/geom/3d/topology/struct/binary/io/vertex/bt_mesh_relative_merge_distance.js +48 -48
  522. package/src/core/geom/3d/topology/struct/binary/io/vertex/bt_mesh_split_pinched_vertices.js +256 -256
  523. package/src/core/geom/3d/topology/struct/binary/io/vertex/bt_mesh_vertex_merge_distance.js +77 -77
  524. package/src/core/geom/3d/topology/struct/binary/io/vertex/bt_vert_fuse_duplicate_edges.js +7 -7
  525. package/src/core/geom/3d/topology/struct/binary/io/vertex/bt_vert_kill.js +7 -7
  526. package/src/core/geom/3d/topology/struct/binary/io/vertex/bt_vertex_fuse_duplicate_edges.js +148 -148
  527. package/src/core/geom/3d/topology/struct/binary/io/vertex/bt_vertex_kill_only.js +7 -7
  528. package/src/core/geom/3d/topology/struct/binary/query/bt_collect_boundary_segments.js +29 -29
  529. package/src/core/geom/3d/topology/struct/binary/query/bt_edge_collapse_breaks_link_condition.js +107 -107
  530. package/src/core/geom/3d/topology/struct/binary/query/bt_edge_flip_is_legal.js +41 -41
  531. package/src/core/geom/3d/topology/struct/binary/query/bt_edge_flip_would_fold.js +120 -120
  532. package/src/core/geom/3d/topology/struct/binary/query/bt_edge_get.js +42 -42
  533. package/src/core/geom/3d/topology/struct/binary/query/bt_edge_is_boundary.js +20 -20
  534. package/src/core/geom/3d/topology/struct/binary/query/bt_edge_other_face.d.ts +32 -0
  535. package/src/core/geom/3d/topology/struct/binary/query/bt_edge_other_face.d.ts.map +1 -0
  536. package/src/core/geom/3d/topology/struct/binary/query/bt_edge_other_face.js +59 -0
  537. package/src/core/geom/3d/topology/struct/binary/query/bt_edge_read_diamond.js +96 -96
  538. package/src/core/geom/3d/topology/struct/binary/query/bt_face_compute_newell_normal.js +72 -72
  539. package/src/core/geom/3d/topology/struct/binary/query/bt_face_get_attached_faces.js +7 -7
  540. package/src/core/geom/3d/topology/struct/binary/query/bt_face_get_edge_adjacent_faces.js +39 -39
  541. package/src/core/geom/3d/topology/struct/binary/query/bt_face_get_edge_adjacent_faces_weighted.js +93 -93
  542. package/src/core/geom/3d/topology/struct/binary/query/bt_face_get_neighbour_faces.js +7 -7
  543. package/src/core/geom/3d/topology/struct/binary/query/bt_face_get_shared_loop.js +48 -48
  544. package/src/core/geom/3d/topology/struct/binary/query/bt_face_island_flood_fill.js +45 -45
  545. package/src/core/geom/3d/topology/struct/binary/query/bt_face_read_triangle.js +66 -66
  546. package/src/core/geom/3d/topology/struct/binary/query/bt_face_third_vertex.d.ts +26 -0
  547. package/src/core/geom/3d/topology/struct/binary/query/bt_face_third_vertex.d.ts.map +1 -0
  548. package/src/core/geom/3d/topology/struct/binary/query/bt_face_third_vertex.js +55 -0
  549. package/src/core/geom/3d/topology/struct/binary/query/bt_faces_shared_loop.js +7 -7
  550. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_assert_valid.js +44 -44
  551. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_build_boundary_euclidean_distance_field.js +32 -32
  552. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_compute_face_islands.js +53 -53
  553. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_face_find_path.js +703 -703
  554. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_sample_interior_grid_points.js +181 -181
  555. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_segment_penetrates_surface.js +133 -133
  556. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_surface_ray_parity.js +82 -82
  557. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_validate.js +599 -599
  558. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_walk_boundary_loops.js +108 -108
  559. package/src/core/geom/3d/topology/struct/binary/query/bt_query_edge_is_boundary.js +7 -7
  560. package/src/core/geom/3d/triangle/computeTriangleClosestPointToPointBarycentric.js +7 -7
  561. package/src/core/geom/3d/triangle/computeTriangleRayIntersection.js +7 -7
  562. package/src/core/geom/3d/triangle/computeTriangleRayIntersectionBarycentric.js +7 -7
  563. package/src/core/geom/3d/triangle/computeTriangleRayIntersectionBarycentricEdge.js +7 -7
  564. package/src/core/geom/3d/triangle/computeTriangleRayIntersectionBarycentricGeometry.js +7 -7
  565. package/src/core/geom/3d/triangle/compute_triangle_area_3d.js +7 -7
  566. package/src/core/geom/3d/triangle/compute_triangle_normal.js +7 -7
  567. package/src/core/geom/3d/triangle/rayTriangleIntersection.js +7 -7
  568. package/src/core/geom/3d/triangle/tri3_closest_point.js +65 -65
  569. package/src/core/geom/3d/triangle/tri3_closest_point_barycentric.js +171 -171
  570. package/src/core/geom/3d/triangle/tri3_flatten_isometric.js +79 -79
  571. package/src/core/geom/3d/triangle/tri3_ray_intersection.js +160 -160
  572. package/src/core/geom/3d/triangle/tri3_ray_intersection_barycentric.js +96 -96
  573. package/src/core/geom/3d/triangle/tri3_raycast.d.ts +42 -0
  574. package/src/core/geom/3d/triangle/tri3_raycast.d.ts.map +1 -0
  575. package/src/core/geom/3d/triangle/tri3_raycast.js +94 -0
  576. package/src/core/geom/3d/triangle/triangle3_incenter.js +7 -7
  577. package/src/core/geom/3d/triangle/triangle_compute_plane_side.js +7 -7
  578. package/src/core/geom/3d/triangle/triangle_compute_signed_volume.js +7 -7
  579. package/src/core/geom/3d/triangle/triangle_intersects_clipping_volume.js +7 -7
  580. package/src/core/geom/3d/triangle/triangle_mesh_compute_signed_volume.js +7 -7
  581. package/src/core/geom/3d/triangle/v3_compute_triangle_normal.js +7 -7
  582. package/src/core/geom/3d/util/make_justified_point_grid.js +7 -7
  583. package/src/core/geom/GEOMETRY_REVIEW_002_API_ISSUES.md +381 -381
  584. package/src/core/geom/GEOMETRY_REVIEW_002_NAMING.md +684 -684
  585. package/src/core/geom/MANIFOLD_COMPARISON_2026_08_01.md +554 -554
  586. package/src/core/geom/mat3/m3_cm_from_translation.js +7 -7
  587. package/src/core/geom/mat3/m3_cm_invert.js +7 -7
  588. package/src/core/geom/mat3/m3_make_rotation.js +5 -5
  589. package/src/core/geom/mat3/m3_make_rotation_TBN.js +31 -31
  590. package/src/core/geom/mat3/m3_multiply_vec3.js +7 -7
  591. package/src/core/geom/packing/computeBoundingSphereOfSpheres.js +7 -7
  592. package/src/core/geom/packing/max-rect/cost/costByBestShortSide.js +7 -7
  593. package/src/core/geom/packing/max-rect/cost/costByRemainingArea.js +7 -7
  594. package/src/core/geom/packing/max-rect/cutArea.js +7 -7
  595. package/src/core/geom/packing/max-rect/findBestContainer.js +7 -7
  596. package/src/core/geom/packing/max-rect/packMaxRectangles.js +7 -7
  597. package/src/core/geom/packing/max-rect/packOneBox.js +7 -7
  598. package/src/core/geom/packing/max-rect/removeRedundantBoxes.js +7 -7
  599. package/src/core/geom/packing/max-rect/removeRedundantBoxesArray.js +7 -7
  600. package/src/core/geom/random/randomPointInBox.js +7 -7
  601. package/src/core/geom/random/randomPointInCircle.js +7 -7
  602. package/src/core/geom/random/randomPointInPoint.js +7 -7
  603. package/src/core/geom/random/randomPointInSphere.js +7 -7
  604. package/src/core/geom/random/randomPointOnBox.js +7 -7
  605. package/src/core/geom/random/randomPointOnSphere.js +7 -7
  606. package/src/core/geom/random/random_orthonormal_basis.js +31 -31
  607. package/src/core/geom/vec/vector_axpy.js +15 -15
  608. package/src/core/geom/vec/vector_axpy_offset.js +20 -20
  609. package/src/core/geom/vec/vector_copy.js +7 -7
  610. package/src/core/geom/vec/vector_dot_offset.js +25 -25
  611. package/src/core/geom/vec/vector_scale_array.js +7 -7
  612. package/src/core/geom/vec2/v2_rotate.js +21 -21
  613. package/src/core/geom/vec2/v2_rotate_cos_sin.js +21 -21
  614. package/src/core/geom/vec3/v3_angle_atan2_between.js +7 -7
  615. package/src/core/geom/vec3/v3_angle_between.js +7 -7
  616. package/src/core/geom/vec3/v3_angle_cos_between.js +7 -7
  617. package/src/core/geom/vec3/v3_array_add_in_place.js +7 -7
  618. package/src/core/geom/vec3/v3_array_centroid.js +7 -7
  619. package/src/core/geom/vec3/v3_array_copy.js +7 -7
  620. package/src/core/geom/vec3/v3_array_displace_in_direction.js +7 -7
  621. package/src/core/geom/vec3/v3_array_equals.js +7 -7
  622. package/src/core/geom/vec3/v3_array_immediate_add.js +7 -7
  623. package/src/core/geom/vec3/v3_array_matrix4_rotate.js +7 -7
  624. package/src/core/geom/vec3/v3_array_normalize.js +7 -7
  625. package/src/core/geom/vec3/v3_array_scale.js +7 -7
  626. package/src/core/geom/vec3/v3_compute_interior_angle.js +7 -7
  627. package/src/core/geom/vec3/v3_cotangent.js +33 -33
  628. package/src/core/geom/vec3/v3_dot_array_array.js +7 -7
  629. package/src/core/geom/vec3/v3_matrix3_multiply.js +30 -30
  630. package/src/core/geom/vec3/v3_matrix3_rotate.js +49 -49
  631. package/src/core/geom/vec3/v3_matrix4_rotate.js +7 -7
  632. package/src/core/geom/vec3/v3_multiply.js +23 -23
  633. package/src/core/geom/vec3/v3_multiply_scalar.js +7 -7
  634. package/src/core/geom/vec3/v3_orthonormal_matrix_from_normal.js +49 -49
  635. package/src/core/geom/vec3/v3_quat3_apply.js +7 -7
  636. package/src/core/geom/vec3/v3_quat3_apply_inverse.js +7 -7
  637. package/src/core/geom/vec3/v3_quaternion_apply.js +39 -39
  638. package/src/core/geom/vec3/v3_quaternion_apply_inverse.js +41 -41
  639. package/src/core/geom/vec3/v3_reflect.js +24 -24
  640. package/src/core/geom/vec3/v3_scale_array.js +18 -18
  641. package/src/core/geom/vec3/v3_triple_cross.js +45 -45
  642. package/src/core/geom/vec3/v3_triple_cross_product.js +7 -7
  643. package/src/core/geom/vec3/v3_uniform_sample_cone.js +21 -21
  644. package/src/core/geom/vec4/v4_multiply_mat4.js +7 -7
  645. package/src/core/graph/csr/csr_graph_build_distance_field.js +112 -112
  646. package/src/core/graph/csr/csr_graph_find_edge.js +25 -25
  647. package/src/core/graph/csr/csr_graph_find_shortest_path.js +105 -105
  648. package/src/core/graph/csr/csr_graph_from_undirected_edge_list.js +94 -94
  649. package/src/core/graph/graph_k_means_cluster.js +250 -250
  650. package/src/core/graph/layout/CircleLayout.js +1 -1
  651. package/src/core/graph/layout/box/BoxLayouter.js +1 -1
  652. package/src/core/graph/layout/graph_peel_topological_layer.js +45 -45
  653. package/src/core/graph/metis/BinaryTopologyFaceGraph.js +49 -49
  654. package/src/core/graph/metis/bt_mesh_build_face_graph.js +111 -111
  655. package/src/core/graph/metis/cluster_mesh_metis.js +137 -137
  656. package/src/core/graph/metis/native/bisection/bisect_graph.js +100 -100
  657. package/src/core/graph/metis/native/bisection/compute_2way_params.js +86 -86
  658. package/src/core/graph/metis/native/bisection/fm_2way.js +297 -297
  659. package/src/core/graph/metis/native/bisection/grow_bisection.js +137 -137
  660. package/src/core/graph/metis/native/bisection/split_graph_two_way.js +119 -119
  661. package/src/core/graph/metis/native/coarsen/coarsen_graph.js +94 -94
  662. package/src/core/graph/metis/native/coarsen/create_coarse_graph.js +158 -158
  663. package/src/core/graph/metis/native/coarsen/match_shem.js +175 -175
  664. package/src/core/graph/metis/native/initial/initial_kway_bfs.js +122 -122
  665. package/src/core/graph/metis/native/initial/initial_kway_recursive_bisection.js +170 -170
  666. package/src/core/graph/metis/native/metis_partition_kway.js +126 -126
  667. package/src/core/graph/metis/native/refine/RefinementScratch.js +53 -53
  668. package/src/core/graph/metis/native/refine/fm_kway.js +487 -487
  669. package/src/core/graph/metis/native/refine/project_kway.js +43 -43
  670. package/src/core/graph/metis/native/refine/refine_kway.js +43 -43
  671. package/src/core/lang/reactive/pegjs/parser.js +3458 -3458
  672. package/src/core/math/complex/complex_horner_eval.js +31 -31
  673. package/src/core/math/computeWholeDivisorLow.js +33 -33
  674. package/src/core/math/f32_next_down.js +24 -24
  675. package/src/core/math/f32_next_up.js +27 -27
  676. package/src/core/math/hash/hash_mix2.js +22 -22
  677. package/src/core/math/hash/hash_mix3.js +19 -19
  678. package/src/core/math/linalg/cg/cg_solve.js +106 -106
  679. package/src/core/math/linalg/cg/cg_solve_normal_equations.js +59 -59
  680. package/src/core/math/linalg/cubic_residual_times_derivative_accumulate.js +43 -43
  681. package/src/core/math/linalg/eigen/matrix_householder_in_place.js +122 -122
  682. package/src/core/math/linalg/eigen/matrix_qr_in_place.js +190 -190
  683. package/src/core/math/linalg/eigen/matrix_symmetric_3x3_eigen.js +117 -117
  684. package/src/core/math/linalg/givens/givens_apply_rows.js +28 -28
  685. package/src/core/math/linalg/givens/givens_apply_rows_offset.js +30 -30
  686. package/src/core/math/linalg/givens/givens_rotation_coefficients.js +39 -39
  687. package/src/core/math/linalg/lu_factor_linear_system.js +108 -108
  688. package/src/core/math/linalg/lu_solve_linear_system.js +53 -53
  689. package/src/core/math/linalg/polynomial_add_into.js +29 -29
  690. package/src/core/math/linalg/polynomial_complex_roots_aberth_ehrlich.js +159 -159
  691. package/src/core/math/linalg/polynomial_cubic_derivative_eval.js +16 -16
  692. package/src/core/math/linalg/polynomial_cubic_horner_eval.js +19 -19
  693. package/src/core/math/linalg/polynomial_cubic_second_derivative_eval.js +14 -14
  694. package/src/core/math/linalg/polynomial_multiply.js +41 -41
  695. package/src/core/math/linalg/polynomial_real_roots_in_interval.js +211 -211
  696. package/src/core/math/linalg/polynomial_root_bound_cauchy.js +30 -30
  697. package/src/core/math/linalg/polynomial_scale_into.js +17 -17
  698. package/src/core/math/linalg/polynomial_sub_into.js +29 -29
  699. package/src/core/math/linalg/solve_linear_system_GEPP_2x2.js +96 -96
  700. package/src/core/math/linalg/sor_optimal_omega.js +42 -42
  701. package/src/core/math/linalg/sparse/SparseMatrixCSR.js +88 -88
  702. package/src/core/math/linalg/sparse/spm_matvec.js +22 -22
  703. package/src/core/math/linalg/sparse/spm_matvec_transpose.js +29 -29
  704. package/src/core/math/lookup/LUT_HEATMAP.js +30 -30
  705. package/src/core/math/lookup/ParameterLookupTable.js +495 -495
  706. package/src/core/math/lookup/ParameterLookupTableFlags.js +6 -6
  707. package/src/core/math/noise/curl_noise_3d.js +18 -18
  708. package/src/core/math/physics/brdf/cone_cosine_from_roughness.js +28 -28
  709. package/src/core/math/physics/brdf/reflection_sample_weight.js +48 -48
  710. package/src/core/math/physics/kinematics/computeInterceptPoint.js +79 -79
  711. package/src/core/math/random/randomSeed.js +16 -16
  712. package/src/core/math/random/random_pick_weighted_index.js +26 -26
  713. package/src/core/math/round_half_away_from_zero.js +13 -13
  714. package/src/core/math/spline/spline3_hermite_apply_transform.js +107 -107
  715. package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite_1d.js +125 -125
  716. package/src/core/math/spline/spline3_hermite_intersects_spline3_hermite.js +50 -50
  717. package/src/core/math/spline/spline3_hermite_reverse.js +49 -49
  718. package/src/core/math/spline/spline3_hermite_to_monomial.js +37 -37
  719. package/src/core/math/statistics/hammersley_sequence.js +53 -53
  720. package/src/core/model/reactive/model/util/createRandomReactiveExpression.js +185 -185
  721. package/src/core/path/convertPathToURL.js +107 -107
  722. package/src/core/process/delay.js +16 -16
  723. package/src/core/science/units/MEASUREMENT_UNITS_SCIENTIFIC.js +87 -87
  724. package/src/core/science/units/MEASUREMENT_UNITS_SI.js +17 -17
  725. package/src/core/science/units/NamedUnit.js +48 -48
  726. package/src/core/science/units/UNIT_DIMENSION_MAPPING.js +29 -29
  727. package/src/core/science/units/UnitDimension.js +28 -28
  728. package/src/core/science/units/format_quantity.js +74 -74
  729. package/src/core/science/units/magnitude_prefix.js +127 -127
  730. package/src/core/science/units/si/AMPERE.js +13 -13
  731. package/src/core/science/units/si/CANDELA.js +13 -13
  732. package/src/core/science/units/si/DIMENSIONLESS.js +14 -14
  733. package/src/core/science/units/si/KELVIN.js +13 -13
  734. package/src/core/science/units/si/KILOGRAM.js +13 -13
  735. package/src/core/science/units/si/METER.js +13 -13
  736. package/src/core/science/units/si/MOLE.js +13 -13
  737. package/src/core/science/units/si/SECOND.js +13 -13
  738. package/src/core/science/units/si/derived/BECQUEREL.js +16 -16
  739. package/src/core/science/units/si/derived/COULOMB.js +13 -13
  740. package/src/core/science/units/si/derived/FARAD.js +13 -13
  741. package/src/core/science/units/si/derived/GRAY.js +16 -16
  742. package/src/core/science/units/si/derived/HENRY.js +13 -13
  743. package/src/core/science/units/si/derived/HERTZ.js +16 -16
  744. package/src/core/science/units/si/derived/JOULE.js +13 -13
  745. package/src/core/science/units/si/derived/KATAL.js +13 -13
  746. package/src/core/science/units/si/derived/LUMEN.js +14 -14
  747. package/src/core/science/units/si/derived/LUX.js +13 -13
  748. package/src/core/science/units/si/derived/NEWTON.js +13 -13
  749. package/src/core/science/units/si/derived/OHM.js +13 -13
  750. package/src/core/science/units/si/derived/PASCAL.js +13 -13
  751. package/src/core/science/units/si/derived/SIEMENS.js +13 -13
  752. package/src/core/science/units/si/derived/SIEVERT.js +16 -16
  753. package/src/core/science/units/si/derived/TESLA.js +13 -13
  754. package/src/core/science/units/si/derived/VOLT.js +13 -13
  755. package/src/core/science/units/si/derived/WATT.js +13 -13
  756. package/src/core/science/units/si/derived/WEBER.js +13 -13
  757. package/src/core/science/units/unit_matrix_to_string.js +403 -403
  758. package/src/engine/Engine.js +726 -726
  759. package/src/engine/EngineHarness.js +590 -590
  760. package/src/engine/REVIEW_2026_07_03.md +211 -211
  761. package/src/engine/animation/AnimatedActions.js +81 -81
  762. package/src/engine/animation/AnimationUtils.js +252 -252
  763. package/src/engine/animation/async/TimeSeries.js +300 -300
  764. package/src/engine/animation/curve/animation_curve_binary_codec.js +60 -60
  765. package/src/engine/animation/curve/draw/canvas_point_to_curve.js +22 -22
  766. package/src/engine/animation/curve/draw/curve_point_to_canvas.js +38 -38
  767. package/src/engine/animation/curve/draw/position_canvas_to_curve.js +22 -22
  768. package/src/engine/animation/curve/draw/position_curve_to_canvas.js +22 -22
  769. package/src/engine/asset/GameAssetType.d.ts +0 -1
  770. package/src/engine/asset/GameAssetType.js +0 -1
  771. package/src/engine/asset/load_model_scene_bundle.js +27 -27
  772. package/src/engine/asset/loaders/font/FontAsset.js +21 -21
  773. package/src/engine/asset/loaders/font/FontAssetLoader.js +20 -20
  774. package/src/engine/asset/loaders/gltf_test_fixtures.js +358 -358
  775. package/src/engine/asset/loaders/image/ImageBitmapAssetLoader.js +52 -52
  776. package/src/engine/asset/loaders/image/ImageRGBADataLoader.js +94 -94
  777. package/src/engine/asset/loaders/image/png/filter/png_filter_unFilterAverage.js +59 -59
  778. package/src/engine/asset/loaders/image/png/filter/png_filter_unFilterNone.js +55 -55
  779. package/src/engine/asset/loaders/image/png/filter/png_filter_unFilterPaeth.js +74 -74
  780. package/src/engine/asset/loaders/image/png/filter/png_filter_unFilterSub.js +34 -34
  781. package/src/engine/asset/loaders/image/png/filter/png_filter_unFilterUp.js +46 -46
  782. package/src/engine/control/first-person/DESIGN.md +640 -640
  783. package/src/engine/control/first-person/DESIGN_EXTENSIONS.md +563 -563
  784. package/src/engine/control/first-person/FirstPersonMotionPhase.js +134 -134
  785. package/src/engine/control/first-person/FirstPersonPlayerControllerSystem.js +2045 -2045
  786. package/src/engine/control/first-person/mastery/DecisionPoint.js +30 -30
  787. package/src/engine/control/first-person/math/Spring.js +71 -71
  788. package/src/engine/control/first-person/math/computeJumpFromApex.js +23 -23
  789. package/src/engine/control/first-person/math/computeLRCBreathRate.js +41 -41
  790. package/src/engine/control/first-person/math/computeMassRatios.js +44 -44
  791. package/src/engine/control/first-person/math/criticallyDampedSpring.js +34 -34
  792. package/src/engine/control/first-person/math/dampedSpringStep.js +72 -72
  793. package/src/engine/control/first-person/math/jumpDynamics.js +108 -108
  794. package/src/engine/control/first-person/math/stepTowards.js +20 -20
  795. package/src/engine/control/first-person/pose/FirstPersonPose.js +137 -137
  796. package/src/engine/control/first-person/pose/FirstPersonPosture.js +27 -27
  797. package/src/engine/control/first-person/test/buildTestPlayer.js +36 -36
  798. package/src/engine/ecs/ASYNC_SYSTEMS_PROPOSAL.md +531 -531
  799. package/src/engine/ecs/EntityManager.d.ts +7 -3
  800. package/src/engine/ecs/EntityManager.d.ts.map +1 -1
  801. package/src/engine/ecs/EntityManager.js +10 -5
  802. package/src/engine/ecs/HIERARCHY_REVIEW.md +717 -717
  803. package/src/engine/ecs/async/SystemWorkerHost.js +329 -329
  804. package/src/engine/ecs/async/SystemWorkerLoopback.js +143 -143
  805. package/src/engine/ecs/async/WORKER_SYSTEM_PLAN.md +246 -246
  806. package/src/engine/ecs/async/WorkerSystem.js +702 -702
  807. package/src/engine/ecs/async/WorkerSystemProtocol.js +150 -150
  808. package/src/engine/ecs/fow/FogOfWar.js +707 -707
  809. package/src/engine/ecs/fow/FogOfWarEdgeMode.js +40 -40
  810. package/src/engine/ecs/fow/fog_of_war_mask_world_bounds.js +24 -24
  811. package/src/engine/ecs/fow/fog_of_war_uv_transform.js +30 -30
  812. package/src/engine/ecs/gui/hud/testHudEdgeStick.js +132 -134
  813. package/src/engine/ecs/hierarchy/EntityChildIndex.js +140 -140
  814. package/src/engine/ecs/ik/IKProblem.js +42 -42
  815. package/src/engine/ecs/ik/InverseKinematicsSystem.js +193 -193
  816. package/src/engine/ecs/ik/OneBoneSurfaceAlignmentSolver.js +134 -134
  817. package/src/engine/ecs/ik/TwoBoneInverseKinematicsSolver.js +97 -97
  818. package/src/engine/ecs/ik/ik_bone_chain.js +85 -85
  819. package/src/engine/ecs/ik/ik_probe_terrain_contact.js +119 -119
  820. package/src/engine/ecs/parent/EntityNode.d.ts +1 -1
  821. package/src/engine/ecs/parent/EntityNode.js +2 -2
  822. package/src/engine/ecs/parent/ParentEntitySystem.js +255 -255
  823. package/src/engine/ecs/speaker/VoiceSystem.d.ts +2 -2
  824. package/src/engine/ecs/speaker/VoiceSystem.d.ts.map +1 -1
  825. package/src/engine/ecs/speaker/VoiceSystem.js +13 -7
  826. package/src/engine/ecs/storage/binary/SERIALIZATION_COVERAGE.md +269 -269
  827. package/src/engine/ecs/storage/binary/object-v2/BinaryObjectDeSerializer.js +315 -315
  828. package/src/engine/ecs/storage/binary/object-v2/BinaryObjectSerializer.js +331 -331
  829. package/src/engine/ecs/storage/binary/object-v2/DESIGN.md +199 -199
  830. package/src/engine/ecs/storage/binary/object-v2/ObjectBinaryFormat.js +57 -57
  831. package/src/engine/ecs/storage/binary/object-v2/ObjectSerializationAdapter.js +56 -56
  832. package/src/engine/ecs/storage/binary/object-v2/README.md +110 -110
  833. package/src/engine/ecs/storage/binary/object-v2/objectStringCodec.js +48 -48
  834. package/src/engine/ecs/storage/binary/object-v2/serializerTestHarness.js +44 -44
  835. package/src/engine/ecs/storage/populateEngineSerializationRegistry.d.ts.map +1 -1
  836. package/src/engine/ecs/storage/populateEngineSerializationRegistry.js +5 -1
  837. package/src/engine/ecs/terrain/TerrainClouds.js +97 -97
  838. package/src/engine/ecs/terrain/ecs/StubTerrainBuildWorker.js +65 -65
  839. package/src/engine/ecs/terrain/ecs/Terrain.d.ts +4 -0
  840. package/src/engine/ecs/terrain/ecs/Terrain.d.ts.map +1 -1
  841. package/src/engine/ecs/terrain/ecs/Terrain.js +790 -790
  842. package/src/engine/ecs/terrain/ecs/TerrainSystem.js +209 -209
  843. package/src/engine/ecs/terrain/ecs/cling/ClingToTerrainSystem.js +316 -316
  844. package/src/engine/ecs/terrain/ecs/layers/TerrainLayers.js +416 -416
  845. package/src/engine/ecs/terrain/ecs/splat/SplatMapping.js +525 -525
  846. package/src/engine/ecs/terrain/overlay/TerrainOverlay.js +302 -302
  847. package/src/engine/ecs/terrain/tiles/TerrainTile.js +561 -561
  848. package/src/engine/ecs/terrain/tiles/TerrainTileManager.js +896 -896
  849. package/src/engine/ecs/terrain/tiles/tile_geometry_to_meshlets.js +36 -36
  850. package/src/engine/ecs/terrain/util/loadVisibleTerrainTiles.js +129 -129
  851. package/src/engine/ecs/transform/Transform64.js +814 -814
  852. package/src/engine/ecs/transform/t64_copy_from_transform.js +35 -35
  853. package/src/engine/ecs/transform/t64_equals_transform.js +55 -55
  854. package/src/engine/ecs/transform/t64_look_rotation.js +36 -36
  855. package/src/engine/ecs/transform/t64_transform_direction.js +25 -25
  856. package/src/engine/ecs/transform/transform_copy_from_t64.js +28 -28
  857. package/src/engine/ecs/transform-attachment/TransformAttachmentSystem.js +345 -345
  858. package/src/engine/ecs/transform-attachment/transform_attachment_find_descendant_by_name.js +65 -65
  859. package/src/engine/ecs/transform-attachment/transform_attachment_parent_of.js +24 -24
  860. package/src/engine/ecs/util/hideEntityGracefully.js +208 -208
  861. package/src/engine/graphics/CONTEXT_LOSS_RECOVERY_PLAN.md +446 -446
  862. package/src/engine/graphics/FULL_SCREEN_QUAD_VERTEX_SHADER.js +14 -14
  863. package/src/engine/graphics/FrameThrottle.js +2 -2
  864. package/src/engine/graphics/ecs/animation/animator/graph/AnimationState.js +207 -207
  865. package/src/engine/graphics/ecs/animation/animator/graph/AnimationTransition.js +151 -151
  866. package/src/engine/graphics/ecs/animation/animator/graph/definition/serialization/AnimationGraphDefinitionAssetLoader.js +22 -22
  867. package/src/engine/graphics/ecs/camera/Camera.js +165 -165
  868. package/src/engine/graphics/ecs/camera/camera_active_frustum.js +25 -25
  869. package/src/engine/graphics/ecs/camera/camera_find_active.js +28 -28
  870. package/src/engine/graphics/ecs/camera/camera_traverse_active.js +18 -18
  871. package/src/engine/graphics/ecs/camera/pp/PerfectPanner.js +170 -170
  872. package/src/engine/graphics/ecs/camera/serialization/CameraSerializationAdapter.js +68 -68
  873. package/src/engine/graphics/ecs/camera/topdown/TopDownCameraController.js +296 -296
  874. package/src/engine/graphics/ecs/decal/v2/Decal.js +220 -220
  875. package/src/engine/graphics/ecs/decal/v2/DecalSerializationAdapter.js +56 -56
  876. package/src/engine/graphics/ecs/decal/v2/DecalSerializationUpgrader_0_1.js +38 -38
  877. package/src/engine/graphics/ecs/light/shadow/compute_view_frustum_aabb_in_space.js +62 -62
  878. package/src/engine/graphics/ecs/light/shadow/extend_shadow_camera_near_for_casters.js +77 -77
  879. package/src/engine/graphics/ecs/make_bvh_depth_range_computer.js +33 -33
  880. package/src/engine/graphics/ecs/mesh-v2/DeferredBoundsQueue.js +88 -88
  881. package/src/engine/graphics/ecs/mesh-v2/ShadedGeometry.js +428 -428
  882. package/src/engine/graphics/ecs/mesh-v2/aggregate/SGMesh.js +303 -303
  883. package/src/engine/graphics/ecs/mesh-v2/aggregate/SGMeshSystem.d.ts +1 -1
  884. package/src/engine/graphics/ecs/mesh-v2/aggregate/serialization/SGMeshSerializationAdapter.js +39 -39
  885. package/src/engine/graphics/ecs/mesh-v2/sg_hierarchy_compute_bounding_box_via_parent_entity.js +36 -36
  886. package/src/engine/graphics/ecs/path/tube/build/GeometryStream.js +94 -94
  887. package/src/engine/graphics/ecs/path/tube/build/build_stream_catmullrom.js +310 -310
  888. package/src/engine/graphics/ecs/path/tube/build/build_stream_linear.js +73 -73
  889. package/src/engine/graphics/ecs/path/tube/build/computeFrenetFrames.js +199 -199
  890. package/src/engine/graphics/ecs/path/tube/build/makeTubeStream.js +217 -217
  891. package/src/engine/graphics/ecs/path/tube/build/make_cap.js +340 -340
  892. package/src/engine/graphics/ecs/path/tube/build/make_ring_faces.js +40 -40
  893. package/src/engine/graphics/ecs/path/tube/build/make_ring_vertices.js +153 -153
  894. package/src/engine/graphics/ecs/render_layers_compute_depth_range.js +64 -64
  895. package/src/engine/graphics/ecs/trail3d/Trail3D.js +483 -483
  896. package/src/engine/graphics/ecs/trail3d/Trail3DFlags.js +26 -26
  897. package/src/engine/graphics/ecs/trail3d/advance_trail.js +42 -42
  898. package/src/engine/graphics/ecs/trail3d/make_gradient_stroke.js +58 -58
  899. package/src/engine/graphics/ecs/trail3d/seed_trail_stroke.js +90 -90
  900. package/src/engine/graphics/ecs/trail3d/seed_trail_tube.js +75 -75
  901. package/src/engine/graphics/ecs/trail3d/trail_head_position.js +19 -19
  902. package/src/engine/graphics/ecs/water/Water.js +104 -104
  903. package/src/engine/graphics/geometry/AttributeData.js +84 -84
  904. package/src/engine/graphics/geometry/buffered/geometry_compute_vertex_normals_indexed.js +49 -49
  905. package/src/engine/graphics/geometry/buffered/query/GeometrySpatialQueryAccelerator.js +288 -288
  906. package/src/engine/graphics/geometry/buffered/query/nearest_point_on_triangle_record.js +87 -87
  907. package/src/engine/graphics/geometry/buffered/read_triangle_vertices.js +102 -102
  908. package/src/engine/graphics/geometry/bvh/buffered/bvh32_from_buffered_geometry.js +52 -52
  909. package/src/engine/graphics/geometry/bvh/buffered/bvh32_from_indexed_geometry.js +20 -20
  910. package/src/engine/graphics/geometry/bvh/buffered/bvh32_from_unindexed_geometry.js +19 -19
  911. package/src/engine/graphics/geometry/optimization/geometry_optimize_uv_tension.js +135 -135
  912. package/src/engine/graphics/impostors/voxel/README.md +149 -149
  913. package/src/engine/graphics/impostors/voxel/VoxelImpostorDescription.js +141 -141
  914. package/src/engine/graphics/particles/node-based/codegen/glsl/getTypeByteSize.js +20 -20
  915. package/src/engine/graphics/particles/node-based/nodes/FloatConstant.d.ts +2 -0
  916. package/src/engine/graphics/particles/node-based/nodes/FloatConstant.d.ts.map +1 -1
  917. package/src/engine/graphics/particles/node-based/nodes/ReadFloatUniform.d.ts +2 -0
  918. package/src/engine/graphics/particles/node-based/nodes/ReadFloatUniform.d.ts.map +1 -1
  919. package/src/engine/graphics/particles/node-based/nodes/ReadVelocity.d.ts +2 -0
  920. package/src/engine/graphics/particles/node-based/nodes/ReadVelocity.d.ts.map +1 -1
  921. package/src/engine/graphics/particles/node-based/nodes/ShaderNode.d.ts +1 -2
  922. package/src/engine/graphics/particles/node-based/nodes/ShaderNode.d.ts.map +1 -1
  923. package/src/engine/graphics/particles/node-based/nodes/Vector3Constant.d.ts +2 -0
  924. package/src/engine/graphics/particles/node-based/nodes/Vector3Constant.d.ts.map +1 -1
  925. package/src/engine/graphics/particles/node-based/nodes/Vector3Merge.d.ts +2 -0
  926. package/src/engine/graphics/particles/node-based/nodes/Vector3Merge.d.ts.map +1 -1
  927. package/src/engine/graphics/particles/node-based/nodes/Vector3Split.d.ts +2 -0
  928. package/src/engine/graphics/particles/node-based/nodes/Vector3Split.d.ts.map +1 -1
  929. package/src/engine/graphics/particles/node-based/nodes/WriteVelocity.d.ts +2 -0
  930. package/src/engine/graphics/particles/node-based/nodes/WriteVelocity.d.ts.map +1 -1
  931. package/src/engine/graphics/particles/node-based/nodes/attribute/ReadVector3Attribute.d.ts +2 -0
  932. package/src/engine/graphics/particles/node-based/nodes/attribute/ReadVector3Attribute.d.ts.map +1 -1
  933. package/src/engine/graphics/particles/node-based/nodes/attribute/WriteVector3Attribute.d.ts +2 -0
  934. package/src/engine/graphics/particles/node-based/nodes/attribute/WriteVector3Attribute.d.ts.map +1 -1
  935. package/src/engine/graphics/particles/node-based/nodes/math/AddFloatNode.d.ts +2 -0
  936. package/src/engine/graphics/particles/node-based/nodes/math/AddFloatNode.d.ts.map +1 -1
  937. package/src/engine/graphics/particles/node-based/nodes/math/Vector3Add.d.ts +2 -0
  938. package/src/engine/graphics/particles/node-based/nodes/math/Vector3Add.d.ts.map +1 -1
  939. package/src/engine/graphics/particles/node-based/nodes/math/Vector3Divide.d.ts +2 -0
  940. package/src/engine/graphics/particles/node-based/nodes/math/Vector3Divide.d.ts.map +1 -1
  941. package/src/engine/graphics/particles/node-based/nodes/math/Vector3Multiply.d.ts +2 -0
  942. package/src/engine/graphics/particles/node-based/nodes/math/Vector3Multiply.d.ts.map +1 -1
  943. package/src/engine/graphics/particles/node-based/nodes/math/Vector3Subtract.d.ts +2 -0
  944. package/src/engine/graphics/particles/node-based/nodes/math/Vector3Subtract.d.ts.map +1 -1
  945. package/src/engine/graphics/particles/node-based/nodes/noise/CurlNoiseNode.d.ts +2 -0
  946. package/src/engine/graphics/particles/node-based/nodes/noise/CurlNoiseNode.d.ts.map +1 -1
  947. package/src/engine/graphics/particles/node-based/nodes/particle_data_type_component_count.js +45 -45
  948. package/src/engine/graphics/particles/particular/engine/ParticularEngine.js +229 -229
  949. package/src/engine/graphics/particles/particular/engine/emitter/ParticleEmitter.d.ts +2 -10
  950. package/src/engine/graphics/particles/particular/engine/emitter/ParticleEmitter.d.ts.map +1 -1
  951. package/src/engine/graphics/particles/particular/engine/emitter/ParticleEmitter.js +1149 -1149
  952. package/src/engine/graphics/particles/particular/engine/emitter/ParticlePool.js +472 -472
  953. package/src/engine/graphics/particles/particular/engine/parameter/ParameterLookupTable.js +2 -2
  954. package/src/engine/graphics/particles/particular/engine/parameter/ParameterLookupTableFlags.js +2 -2
  955. package/src/engine/graphics/particles/particular/engine/parameter/ParameterSet.d.ts.map +1 -1
  956. package/src/engine/graphics/particles/particular/engine/parameter/ParameterSet.js +3 -5
  957. package/src/engine/graphics/particles/particular/engine/shader/ShaderManager.js +135 -135
  958. package/src/engine/graphics/particles/particular/engine/utils/distribute_points_on_indexed_triangles.js +168 -168
  959. package/src/engine/graphics/render/forward_plus/plugin/forwardPlusMaterialMarker.js +18 -18
  960. package/src/engine/graphics/render/forward_plus/read_frustum_corner.js +2 -2
  961. package/src/engine/graphics/render/frame_graph/FRAME_GRAPH_RECOMMENDATIONS.md +219 -219
  962. package/src/engine/graphics/render/frame_graph/FrameGraph.js +900 -900
  963. package/src/engine/graphics/render/frame_graph/FrameGraphScope.js +72 -72
  964. package/src/engine/graphics/render/frame_graph/FrameGraphState.js +39 -39
  965. package/src/engine/graphics/render/frame_graph/ResourceEntry.js +96 -96
  966. package/src/engine/graphics/render/gizmo/GizmoShapeBuffer.js +105 -105
  967. package/src/engine/graphics/render/gizmo/GizmoShapeRenderingInterface.js +162 -162
  968. package/src/engine/graphics/render/layers/RenderLayer.js +95 -95
  969. package/src/engine/graphics/sh3/build_injected_shader.js +56 -56
  970. package/src/engine/graphics/sh3/grid_dilate_average.js +175 -175
  971. package/src/engine/graphics/sh3/path_tracer/BufferedGeometryBVH.js +506 -506
  972. package/src/engine/graphics/sh3/path_tracer/PathTracedMesh.js +161 -161
  973. package/src/engine/graphics/sh3/path_tracer/PathTracedRenderer.js +205 -205
  974. package/src/engine/graphics/sh3/path_tracer/PathTracedScene.js +698 -698
  975. package/src/engine/graphics/sh3/path_tracer/REVIEW_001_ACTION_PLAN.md +490 -490
  976. package/src/engine/graphics/sh3/path_tracer/geometry/compute_triangle_group_aabb3.js +2 -2
  977. package/src/engine/graphics/sh3/path_tracer/make_sky_hosek.js +45 -45
  978. package/src/engine/graphics/sh3/path_tracer/populate_path_traced_scene_from_ecd.js +73 -73
  979. package/src/engine/graphics/sh3/path_tracer/ray_hit_apply_transform.js +32 -32
  980. package/src/engine/graphics/sh3/path_tracer/texture/apply_texture_clamping_to_coordinate.js +28 -28
  981. package/src/engine/graphics/sh3/path_tracer/texture/sample_material.js +142 -142
  982. package/src/engine/graphics/sh3/sky/hosek/render_hosek_sky_to_equirectangular.js +70 -70
  983. package/src/engine/graphics/sh3/sky/hosek/sky_hosek_compute_irradiance_by_direction.js +388 -388
  984. package/src/engine/graphics/shaders/chunks/glsl_octahedral_impostor.js +277 -277
  985. package/src/engine/graphics/shaders/chunks/glsl_voxel_impostor_splat.js +57 -57
  986. package/src/engine/graphics/shadows/vsm.glsl.js +43 -43
  987. package/src/engine/graphics/texture/3d/scs3d_sample_linear3.js +81 -81
  988. package/src/engine/graphics/texture/EnvironmentTextureProjection.js +15 -15
  989. package/src/engine/graphics/texture/channel_count_to_format_prefix.js +26 -26
  990. package/src/engine/graphics/texture/isImageBitmap.js +2 -2
  991. package/src/engine/graphics/texture/reflection/convolve_equirectangular_reflection.js +189 -189
  992. package/src/engine/graphics/texture/reflection/equirectangular_reflection_roughness.js +51 -51
  993. package/src/engine/graphics/texture/sampler/HarmonicDiffusionGrid.js +145 -145
  994. package/src/engine/graphics/texture/sampler/Sampler2DArray.js +172 -172
  995. package/src/engine/graphics/texture/sampler/compare/FLIP_IMPLEMENTATION_PLAN.md +466 -466
  996. package/src/engine/graphics/texture/sampler/compare/flip/FlipParameters.js +18 -18
  997. package/src/engine/graphics/texture/sampler/compare/flip/FlipPooling.js +33 -33
  998. package/src/engine/graphics/texture/sampler/compare/flip/flip_build_feature_kernel.js +147 -147
  999. package/src/engine/graphics/texture/sampler/compare/flip/flip_build_spatial_kernel.js +177 -177
  1000. package/src/engine/graphics/texture/sampler/compare/flip/flip_compute_color_difference.js +104 -104
  1001. package/src/engine/graphics/texture/sampler/compare/flip/flip_compute_feature_difference.js +76 -76
  1002. package/src/engine/graphics/texture/sampler/compare/flip/flip_compute_pixels_per_degree.js +34 -34
  1003. package/src/engine/graphics/texture/sampler/compare/flip/flip_constants.js +77 -77
  1004. package/src/engine/graphics/texture/sampler/compare/flip/flip_filter_feature.js +48 -48
  1005. package/src/engine/graphics/texture/sampler/compare/flip/flip_filter_spatial.js +86 -86
  1006. package/src/engine/graphics/texture/sampler/compare/flip/flip_pool.js +71 -71
  1007. package/src/engine/graphics/texture/sampler/compare/flip/flip_srgb_to_ycxcz.js +55 -55
  1008. package/src/engine/graphics/texture/sampler/compare/flip/sampler2d_flip_ldr.js +80 -80
  1009. package/src/engine/graphics/texture/sampler/filter/SeparableKernel.js +82 -82
  1010. package/src/engine/graphics/texture/sampler/filter/sampler2d_convolve_separable.js +122 -122
  1011. package/src/engine/graphics/texture/sampler/resize/sampler2d_scale_down_lanczos.js +148 -148
  1012. package/src/engine/graphics/texture/sampler/sampler2d_channel_apply_lut.js +82 -82
  1013. package/src/engine/graphics/texture/sampler/sampler2d_combine.js +67 -67
  1014. package/src/engine/graphics/texture/sampler/sampler2d_linear_f16_to_srgb_u8.js +54 -54
  1015. package/src/engine/graphics/texture/sampler/sampler2d_sample_equirectangular_direction.js +63 -63
  1016. package/src/engine/graphics/texture/sampler/sampler2d_srgb_to_linear.js +33 -33
  1017. package/src/engine/graphics/texture/sampler/sampler2d_srgb_u8_to_linear_f16.js +68 -68
  1018. package/src/engine/graphics/texture/shade_image_preview.js +103 -103
  1019. package/src/engine/graphics/trail/TrailSpawnMode.js +20 -20
  1020. package/src/engine/graphics/trail/tube/TubeX.js +573 -573
  1021. package/src/engine/graphics/trail/tube/TubeXMaterialSpec.js +58 -58
  1022. package/src/engine/graphics/trail/tube/simulator/TubeXFixedPhysicsSimulator.js +34 -34
  1023. package/src/engine/graphics/trail/tube/tube_attributes_spec.js +69 -69
  1024. package/src/engine/graphics/trail/tube/tube_frame.js +83 -83
  1025. package/src/engine/graphics3/AnimationGraphSystem3.js +329 -329
  1026. package/src/engine/graphics3/AnimationSystem3.js +352 -352
  1027. package/src/engine/graphics3/CameraSystem3.js +170 -170
  1028. package/src/engine/graphics3/DecalSystem3.js +441 -441
  1029. package/src/engine/graphics3/FogOfWarSystem3.js +265 -265
  1030. package/src/engine/graphics3/GraphicsEngine3.js +776 -776
  1031. package/src/engine/graphics3/HighlightSystem3.js +241 -241
  1032. package/src/engine/graphics3/LightSystem3.js +211 -211
  1033. package/src/engine/graphics3/MeshSystem3.js +646 -646
  1034. package/src/engine/graphics3/ParticleEmitterSystem3.js +289 -289
  1035. package/src/engine/graphics3/PathDisplaySystem3.js +309 -309
  1036. package/src/engine/graphics3/PickingQuery.js +48 -48
  1037. package/src/engine/graphics3/PickingResult.js +47 -47
  1038. package/src/engine/graphics3/PickingSystem3.js +273 -273
  1039. package/src/engine/graphics3/ShadeCameraAdapter.js +55 -55
  1040. package/src/engine/graphics3/ShadedGeometrySystem3.js +169 -169
  1041. package/src/engine/graphics3/TerrainSystem3.js +566 -566
  1042. package/src/engine/graphics3/TooltipComponentSystem3.js +328 -328
  1043. package/src/engine/graphics3/Trail3DSystem3.js +221 -221
  1044. package/src/engine/graphics3/VolumetricLightMap.d.ts +90 -0
  1045. package/src/engine/graphics3/VolumetricLightMap.d.ts.map +1 -0
  1046. package/src/engine/graphics3/VolumetricLightMap.js +160 -0
  1047. package/src/engine/graphics3/VolumetricLightMapSerializationAdapter.d.ts +32 -0
  1048. package/src/engine/graphics3/VolumetricLightMapSerializationAdapter.d.ts.map +1 -0
  1049. package/src/engine/graphics3/VolumetricLightMapSerializationAdapter.js +67 -0
  1050. package/src/engine/graphics3/VolumetricLightMapSystem3.d.ts +98 -0
  1051. package/src/engine/graphics3/VolumetricLightMapSystem3.d.ts.map +1 -0
  1052. package/src/engine/graphics3/VolumetricLightMapSystem3.js +261 -0
  1053. package/src/engine/graphics3/WaterSystem3.js +293 -293
  1054. package/src/engine/graphics3/animation/ClipPlayback.js +84 -84
  1055. package/src/engine/graphics3/animation/wrap_clip_time.js +23 -23
  1056. package/src/engine/graphics3/animation/write_graph_playback.js +48 -48
  1057. package/src/engine/graphics3/camera_sync_from_transform.js +29 -29
  1058. package/src/engine/graphics3/debug/gizmo_view_from_camera.js +48 -48
  1059. package/src/engine/graphics3/decal/DECAL_CLUSTER.js +61 -61
  1060. package/src/engine/graphics3/decal/DECAL_GPU_RECORD.js +117 -117
  1061. package/src/engine/graphics3/decal/DecalAtlas.js +194 -194
  1062. package/src/engine/graphics3/decal/DecalAtlasFill.js +32 -32
  1063. package/src/engine/graphics3/decal/GPUDecalRenderer.js +360 -360
  1064. package/src/engine/graphics3/decal/chunk_decal_intersects_frustum.js +59 -59
  1065. package/src/engine/graphics3/decal/chunk_decal_sampling_info.js +57 -57
  1066. package/src/engine/graphics3/decal/chunk_decal_surface_frame.js +54 -54
  1067. package/src/engine/graphics3/decal/decal_atlas_border.js +69 -69
  1068. package/src/engine/graphics3/decal/decal_gpu_records.js +120 -120
  1069. package/src/engine/graphics3/decal/decal_material_samplers.js +109 -109
  1070. package/src/engine/graphics3/decal/graph_build_decal_clusters.js +212 -212
  1071. package/src/engine/graphics3/decal/shader_cluster_assign_decals.js +207 -207
  1072. package/src/engine/graphics3/decal/shader_cull_decals_frustum.js +108 -108
  1073. package/src/engine/graphics3/highlight/GPUHighlightRenderer.js +151 -151
  1074. package/src/engine/graphics3/highlight/HIGHLIGHT_OUTLINE.js +23 -23
  1075. package/src/engine/graphics3/highlight/chunk_highlight_lookup.js +53 -53
  1076. package/src/engine/graphics3/highlight/pack_highlight_color.js +44 -44
  1077. package/src/engine/graphics3/highlight/pack_highlight_table.js +74 -74
  1078. package/src/engine/graphics3/highlight/shader_highlight_outline.js +150 -150
  1079. package/src/engine/graphics3/load_cube_environment.js +68 -68
  1080. package/src/engine/graphics3/make_default_environment.js +107 -107
  1081. package/src/engine/graphics3/particles/particle_gpu_records.js +221 -221
  1082. package/src/engine/graphics3/path/build_tube_path_streams.js +98 -98
  1083. package/src/engine/graphics3/path/tube_stream_write_geometry.js +90 -90
  1084. package/src/engine/graphics3/preview/ModelPreviewFraming.js +35 -35
  1085. package/src/engine/graphics3/preview/make_model_preview_scene.js +154 -154
  1086. package/src/engine/graphics3/preview/make_model_thumbnail.js +48 -48
  1087. package/src/engine/graphics3/preview/model_preview_framing.js +70 -70
  1088. package/src/engine/graphics3/shade_bundle_bounds.js +55 -55
  1089. package/src/engine/graphics3/shade_camera_projection_ray.js +77 -77
  1090. package/src/engine/graphics3/shade_node_to_entity_composition.js +84 -84
  1091. package/src/engine/graphics3/terrain/GPUTerrainSplatRenderer.js +481 -481
  1092. package/src/engine/graphics3/terrain/chunk_terrain_grid_overlay.js +61 -61
  1093. package/src/engine/graphics3/terrain/chunk_terrain_splat_mix.js +66 -66
  1094. package/src/engine/graphics3/terrain/pack_terrain_row_table.js +53 -53
  1095. package/src/engine/graphics3/terrain/shader_terrain_splat.js +215 -215
  1096. package/src/engine/graphics3/trail/trail_tube_write_geometry.js +158 -158
  1097. package/src/engine/input/GAMEPAD_API_PROPOSAL.md +717 -717
  1098. package/src/engine/input/analog/apply_radial_dead_zone.js +38 -38
  1099. package/src/engine/input/devices/GamepadDevice.js +466 -466
  1100. package/src/engine/input/devices/ManualInputDevice.js +152 -152
  1101. package/src/engine/input/devices/PointerCoordinates.js +26 -26
  1102. package/src/engine/input/devices/events/GamepadEvents.js +10 -10
  1103. package/src/engine/input/devices/gamepad/GamepadAxes.js +19 -19
  1104. package/src/engine/input/devices/gamepad/GamepadButtons.js +29 -29
  1105. package/src/engine/input/devices/gamepad/GamepadCoordinates.js +17 -17
  1106. package/src/engine/input/devices/gamepad/GamepadDeviceLayout.js +323 -323
  1107. package/src/engine/input/devices/gamepad/GamepadHandle.js +286 -286
  1108. package/src/engine/input/devices/gamepad/GamepadLayout.js +160 -160
  1109. package/src/engine/input/devices/gamepad/GamepadStandardMapping.js +31 -31
  1110. package/src/engine/input/devices/gamepad/GamepadStick.js +169 -169
  1111. package/src/engine/input/devices/gamepad/GamepadStickCalibration.js +119 -119
  1112. package/src/engine/input/devices/gamepad/GamepadStickCalibrator.js +250 -250
  1113. package/src/engine/input/devices/gamepad/gamepad_layout_database.js +144 -144
  1114. package/src/engine/input/devices/gamepad/gamepad_read_hat_axis.js +61 -61
  1115. package/src/engine/input/devices/gamepad/gamepad_write_dpad_vector.js +30 -30
  1116. package/src/engine/input/devices/gamepad/prototypeGamepadTester.js +2124 -2124
  1117. package/src/engine/input/devices/mouse/MouseButtons.js +22 -22
  1118. package/src/engine/input/ecs/components/Input.js +118 -118
  1119. package/src/engine/input/ecs/components/InputController.js +78 -78
  1120. package/src/engine/input/ecs/ism/ISM_DESIGN.md +1523 -1523
  1121. package/src/engine/input/ecs/ism/ISM_IMPLEMENTATION_PLAN.md +627 -627
  1122. package/src/engine/input/ecs/ism/InputActionEvent.js +40 -40
  1123. package/src/engine/input/ecs/ism/InputBinding.js +71 -71
  1124. package/src/engine/input/ecs/ism/InputCoordinateKind.js +25 -25
  1125. package/src/engine/input/ecs/ism/InputCoordinateSource.js +86 -86
  1126. package/src/engine/input/ecs/ism/InputDirection.js +33 -33
  1127. package/src/engine/input/ecs/ism/InputMapProgram.js +236 -236
  1128. package/src/engine/input/ecs/ism/InputMapSystem.js +976 -976
  1129. package/src/engine/input/ecs/ism/InputOcclusion.js +31 -31
  1130. package/src/engine/input/ecs/ism/InputSource.js +67 -67
  1131. package/src/engine/input/ecs/ism/InputSourceTable.js +107 -107
  1132. package/src/engine/input/ecs/ism/coordinate/InputCoordinateChannel.js +93 -93
  1133. package/src/engine/input/ecs/ism/coordinate/InputCoordinateChannelType.js +11 -11
  1134. package/src/engine/input/ecs/ism/coordinate/InputCoordinateDevice.js +153 -153
  1135. package/src/engine/input/ecs/ism/coordinate/InputCoordinateSwitches.js +160 -160
  1136. package/src/engine/input/ecs/ism/device/GAMEPAD_INPUT_DEVICE_ID.js +9 -9
  1137. package/src/engine/input/ecs/ism/device/GamepadInputDeviceAdapter.js +88 -88
  1138. package/src/engine/input/ecs/ism/device/InputDeviceAdapter.js +72 -72
  1139. package/src/engine/input/ecs/ism/device/KEYBOARD_INPUT_DEVICE_ID.js +9 -9
  1140. package/src/engine/input/ecs/ism/device/KeyboardInputDeviceAdapter.js +63 -63
  1141. package/src/engine/input/ecs/ism/device/ManualInputDeviceAdapter.js +55 -55
  1142. package/src/engine/input/ecs/ism/device/POINTER_INPUT_DEVICE_ID.js +9 -9
  1143. package/src/engine/input/ecs/ism/device/PointerInputDeviceAdapter.js +160 -160
  1144. package/src/engine/input/ecs/ism/map/InputBinding.js +88 -88
  1145. package/src/engine/input/ecs/ism/map/InputCoordinateBinding.js +102 -102
  1146. package/src/engine/input/ecs/ism/map/InputLayer.js +220 -220
  1147. package/src/engine/input/ecs/ism/map/InputMap.js +331 -331
  1148. package/src/engine/input/ecs/ism/prototypeInputMap.js +309 -309
  1149. package/src/engine/input/ecs/ism/trigger/InputTrigger.js +148 -148
  1150. package/src/engine/input/ecs/ism/trigger/InputTriggerChord.js +173 -173
  1151. package/src/engine/input/ecs/ism/trigger/InputTriggerGamepadButton.js +37 -37
  1152. package/src/engine/input/ecs/ism/trigger/InputTriggerHold.js +176 -176
  1153. package/src/engine/input/ecs/ism/trigger/InputTriggerKey.js +39 -39
  1154. package/src/engine/input/ecs/ism/trigger/InputTriggerMouseButton.js +38 -38
  1155. package/src/engine/input/ecs/ism/trigger/InputTriggerRelease.js +120 -120
  1156. package/src/engine/input/ecs/ism/trigger/InputTriggerSequence.js +315 -315
  1157. package/src/engine/input/ecs/ism/trigger/InputTriggerSwitch.js +105 -105
  1158. package/src/engine/input/ecs/ism/trigger/InputTriggerType.js +17 -17
  1159. package/src/engine/input/ecs/systems/InputControllerSystem.js +253 -253
  1160. package/src/engine/input/ecs/systems/InputSystem.js +162 -162
  1161. package/src/engine/intelligence/behavior/primitive/FailingBehaviorSerializationAdapter.js +25 -25
  1162. package/src/engine/intelligence/behavior/primitive/SucceedingBehaviorSerializationAdapter.js +26 -26
  1163. package/src/engine/intelligence/behavior/util/AsynchronousDelayAction.js +55 -55
  1164. package/src/engine/interpolation/BinaryInterpolationAdapter.js +56 -56
  1165. package/src/engine/interpolation/Interpoland.js +49 -49
  1166. package/src/engine/interpolation/Interpolated.js +149 -149
  1167. package/src/engine/interpolation/PoseInterpolationAdapter.js +61 -61
  1168. package/src/engine/interpolation/TransformPoseSerializationAdapter.js +57 -57
  1169. package/src/engine/interpolation/pose_interpoland.js +27 -27
  1170. package/src/engine/navigation/ecs/path_following/PathFollower.js +222 -222
  1171. package/src/engine/navigation/mesh/NavigationMesh.js +145 -145
  1172. package/src/engine/navigation/mesh/OVERLAPPING_SURFACES_2026_08_25.md +661 -0
  1173. package/src/engine/navigation/mesh/PATHFINDING_PLAN.md +229 -229
  1174. package/src/engine/navigation/mesh/build/clip_soup_against_overhangs.d.ts.map +1 -1
  1175. package/src/engine/navigation/mesh/build/clip_soup_against_overhangs.js +628 -520
  1176. package/src/engine/network/CONGESTION_CONTROL.md +87 -87
  1177. package/src/engine/network/README.md +52 -52
  1178. package/src/engine/network/adapters/QuaternionInterpolationAdapter.js +43 -43
  1179. package/src/engine/network/adapters/TransformInterpolationAdapter.js +84 -84
  1180. package/src/engine/network/adapters/TransformReplicationAdapter.js +87 -87
  1181. package/src/engine/network/adapters/Vector3InterpolationAdapter.js +46 -46
  1182. package/src/engine/network/convertPathToURL.js +2 -2
  1183. package/src/engine/network/core/MalformedPacketError.js +26 -26
  1184. package/src/engine/network/core/quantize/quantize_float.js +73 -73
  1185. package/src/engine/network/core/quantize/quantize_position.js +65 -65
  1186. package/src/engine/network/core/sequence/ack_bitfield.js +77 -77
  1187. package/src/engine/network/core/sequence/seq16.js +69 -69
  1188. package/src/engine/network/core/sequence/seq32.js +73 -73
  1189. package/src/engine/network/ecs/NetworkSystem.js +84 -84
  1190. package/src/engine/network/ecs/components/NetworkIdentity.js +73 -73
  1191. package/src/engine/network/ecs/owner_authorization.js +55 -55
  1192. package/src/engine/network/ecs/serialization/NetworkIdentitySerializationAdapter.js +64 -64
  1193. package/src/engine/network/replication/ScopeFilter.js +71 -71
  1194. package/src/engine/network/sim/ActionLog.js +152 -152
  1195. package/src/engine/network/sim/SimAction.js +273 -273
  1196. package/src/engine/network/sim/Snapshotter.js +222 -222
  1197. package/src/engine/network/state/FrameBufferRing.js +168 -168
  1198. package/src/engine/network/state/InputRing.js +134 -134
  1199. package/src/engine/network/state/PriorityAccumulator.js +180 -180
  1200. package/src/engine/network/time/TimeDilation.js +62 -62
  1201. package/src/engine/network/transport/Channel.js +336 -336
  1202. package/src/engine/network/transport/Transport.js +203 -203
  1203. package/src/engine/network/transport/adapters/NodeUDPTransport.js +186 -186
  1204. package/src/engine/network/transport/adapters/WebRTCDataChannelTransport.js +105 -105
  1205. package/src/engine/network/transport/fragments/fragment_send.js +147 -147
  1206. package/src/engine/network/transport/fragments/packet_size.js +101 -101
  1207. package/src/engine/network/xhr.js +23 -23
  1208. package/src/engine/physics/BULLET_REVIEW.md +945 -945
  1209. package/src/engine/physics/CANNON_REVIEW.md +1300 -1300
  1210. package/src/engine/physics/CONSTRAINT_SOLVER_BENCH_LOG.md +208 -208
  1211. package/src/engine/physics/CONSTRAINT_SOLVER_IMPROVEMENTS_PLAN.md +364 -364
  1212. package/src/engine/physics/INTEPOLATION_SYSTEM_PLAN.md +287 -287
  1213. package/src/engine/physics/JOLT_REVIEW.md +913 -913
  1214. package/src/engine/physics/PLAN.md +1120 -1094
  1215. package/src/engine/physics/RAPIER_REVIEW.md +934 -934
  1216. package/src/engine/physics/REVIEW_001_ACTION_PLAN.md +642 -642
  1217. package/src/engine/physics/REVIEW_002.md +151 -151
  1218. package/src/engine/physics/REVIEW_003.md +166 -166
  1219. package/src/engine/physics/broadphase/compute_fat_world_aabb.js +97 -97
  1220. package/src/engine/physics/constraint/DofMode.js +35 -35
  1221. package/src/engine/physics/constraint/solve_constraints.js +1 -1
  1222. package/src/engine/physics/contact/combine_material.js +35 -35
  1223. package/src/engine/physics/ecs/BodyKind.js +29 -29
  1224. package/src/engine/physics/ecs/Collider.js +177 -177
  1225. package/src/engine/physics/ecs/ColliderFlags.js +15 -15
  1226. package/src/engine/physics/ecs/ColliderSerializationAdapter.js +37 -37
  1227. package/src/engine/physics/ecs/Joint.js +475 -475
  1228. package/src/engine/physics/ecs/JointSerializationAdapter.js +72 -72
  1229. package/src/engine/physics/ecs/PhysicsSystem.d.ts +3 -0
  1230. package/src/engine/physics/ecs/PhysicsSystem.d.ts.map +1 -1
  1231. package/src/engine/physics/ecs/PhysicsSystem.js +7 -2
  1232. package/src/engine/physics/ecs/RigidBodySerializationUpgrader_0_1.js +37 -37
  1233. package/src/engine/physics/ecs/SleepState.js +12 -12
  1234. package/src/engine/physics/ecs/find_non_finite_physics_state.js +76 -76
  1235. package/src/engine/physics/fluid/FluidField.js +867 -867
  1236. package/src/engine/physics/fluid/FluidSimulator.js +832 -832
  1237. package/src/engine/physics/fluid/REVIEW_02_PLAN.md +243 -243
  1238. package/src/engine/physics/fluid/SliceVisualiser.js +190 -190
  1239. package/src/engine/physics/fluid/ecs/FluidComponent.js +239 -239
  1240. package/src/engine/physics/fluid/ecs/FluidEffectorsComponent.js +89 -89
  1241. package/src/engine/physics/fluid/ecs/FluidObstacle.js +97 -97
  1242. package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
  1243. package/src/engine/physics/fluid/ecs/FluidObstacleSystem.js +348 -348
  1244. package/src/engine/physics/fluid/ecs/FluidSystem.js +142 -142
  1245. package/src/engine/physics/fluid/ecs/FluidWorkerCore.js +123 -123
  1246. package/src/engine/physics/fluid/ecs/WorkerFluidSystem.js +395 -395
  1247. package/src/engine/physics/fluid/ecs/fluid.worker.js +12 -12
  1248. package/src/engine/physics/fluid/ecs/fluid_build_world_to_grid.js +32 -32
  1249. package/src/engine/physics/fluid/ecs/fluid_gather_visible_effectors.js +47 -47
  1250. package/src/engine/physics/fluid/ecs/fluid_reanchor_field.js +85 -85
  1251. package/src/engine/physics/fluid/ecs/fluid_sync_effectors_from_transform.js +16 -16
  1252. package/src/engine/physics/fluid/ecs/fluid_worker_protocol.js +111 -111
  1253. package/src/engine/physics/fluid/ecs/makeFluidWorker.js +18 -18
  1254. package/src/engine/physics/fluid/effector/AbstractFluidEffector.js +81 -81
  1255. package/src/engine/physics/fluid/effector/GlobalFluidEffector.js +202 -202
  1256. package/src/engine/physics/fluid/effector/ImpulseFluidEffector.js +257 -257
  1257. package/src/engine/physics/fluid/effector/WakeFluidEffector.js +405 -405
  1258. package/src/engine/physics/fluid/prototype.js +126 -126
  1259. package/src/engine/physics/fluid/solver/v3_grid_advect_maccormack_scalar.js +143 -143
  1260. package/src/engine/physics/fluid/solver/v3_grid_advect_maccormack_velocity.js +162 -162
  1261. package/src/engine/physics/fluid/solver/v3_grid_advect_sl_velocity.js +107 -107
  1262. package/src/engine/physics/fluid/solver/v3_grid_apply_vorticity_confinement.js +131 -131
  1263. package/src/engine/physics/fluid/solver/v3_grid_compute_solid_neighbour_mask.js +90 -90
  1264. package/src/engine/physics/fluid/solver/v3_grid_limit8_at_indices.js +38 -38
  1265. package/src/engine/physics/fluid/solver/v3_grid_patch_edges_constant.js +77 -77
  1266. package/src/engine/physics/fluid/solver/v3_grid_patch_edges_uniform.js +113 -113
  1267. package/src/engine/physics/fluid/solver/v3_grid_sample_scalar_masked.js +92 -92
  1268. package/src/engine/physics/fluid/solver/v3_grid_shift_in_place.js +107 -107
  1269. package/src/engine/physics/fluid/solver/v3_grid_solve_pressure.js +135 -135
  1270. package/src/engine/physics/fluid/solver/v3_grid_solve_pressure_pcg.js +511 -511
  1271. package/src/engine/physics/fluid/solver/v3_grid_subtract_pressure_gradient.js +108 -108
  1272. package/src/engine/physics/fluid/solver/v3_mac_advect_maccormack_velocity.js +317 -317
  1273. package/src/engine/physics/fluid/solver/v3_mac_advect_scalar.js +236 -236
  1274. package/src/engine/physics/fluid/solver/v3_mac_advect_sl_velocity.js +217 -217
  1275. package/src/engine/physics/fluid/solver/v3_mac_apply_vorticity_confinement.js +174 -174
  1276. package/src/engine/physics/fluid/solver/v3_mac_clip_trace.js +95 -95
  1277. package/src/engine/physics/fluid/solver/v3_mac_compute_divergence.js +77 -77
  1278. package/src/engine/physics/fluid/solver/v3_mac_compute_face_solid.js +131 -131
  1279. package/src/engine/physics/fluid/solver/v3_mac_subtract_pressure_gradient.js +104 -104
  1280. package/src/engine/physics/inertia/world_inverse_inertia.js +160 -160
  1281. package/src/engine/physics/integration/integrate_position.js +97 -97
  1282. package/src/engine/physics/narrowphase/box_box_manifold.js +681 -681
  1283. package/src/engine/physics/narrowphase/box_triangle_contact.js +906 -906
  1284. package/src/engine/physics/narrowphase/capsule_triangle_contact.js +395 -395
  1285. package/src/engine/physics/narrowphase/compute_penetration.js +369 -369
  1286. package/src/engine/physics/narrowphase/decomposition/decompose_to_triangles.js +55 -55
  1287. package/src/engine/physics/narrowphase/decomposition/triangle_buffer_layout.js +37 -37
  1288. package/src/engine/physics/narrowphase/mesh_mesh_tet_manifold.js +117 -117
  1289. package/src/engine/physics/narrowphase/narrowphase_step.js +1738 -1738
  1290. package/src/engine/physics/narrowphase/reduce_manifold_contacts.js +74 -74
  1291. package/src/engine/physics/narrowphase/refine_ray_hit.d.ts +12 -11
  1292. package/src/engine/physics/narrowphase/refine_ray_hit.d.ts.map +1 -1
  1293. package/src/engine/physics/narrowphase/refine_ray_hit.js +228 -36
  1294. package/src/engine/physics/narrowphase/sphere_sphere_contact.js +52 -52
  1295. package/src/engine/physics/narrowphase/sphere_triangle_contact.js +138 -138
  1296. package/src/engine/physics/persistence/solver_caches.js +309 -309
  1297. package/src/engine/physics/queries/overlap_shape.js +187 -187
  1298. package/src/engine/physics/queries/raycast.d.ts +7 -4
  1299. package/src/engine/physics/queries/raycast.d.ts.map +1 -1
  1300. package/src/engine/physics/queries/raycast.js +11 -5
  1301. package/src/engine/physics/queries/shape_cast.js +417 -417
  1302. package/src/engine/physics/solver/apply_impulse_to_body.js +86 -86
  1303. package/src/engine/physics/solver/friction_cone.js +37 -37
  1304. package/src/engine/physics/solver/solve_contacts.js +1 -1
  1305. package/src/engine/platform/WebEnginePlatform.d.ts.map +1 -1
  1306. package/src/engine/platform/WebEnginePlatform.js +1 -2
  1307. package/src/engine/scene/SerializedScene.d.ts +2 -2
  1308. package/src/engine/scene/SerializedScene.d.ts.map +1 -1
  1309. package/src/engine/scene/transitionToScene.js +121 -121
  1310. package/src/engine/simulation/Ticker.d.ts.map +1 -1
  1311. package/src/engine/simulation/Ticker.js +33 -0
  1312. package/src/engine/sound/ecs/audio/LiveEmitterSet.js +345 -345
  1313. package/src/engine/sound/ecs/audio/SpatialAudioIndex.js +140 -140
  1314. package/src/engine/sound/simulation/AcousticSimulator.js +646 -646
  1315. package/src/engine/sound/simulation/IMPLEMENTATION_PLAN.md +432 -432
  1316. package/src/engine/sound/simulation/PROBE_PLACEMENT.md +250 -250
  1317. package/src/engine/sound/simulation/PROBE_TRANSFER_RESEARCH.md +322 -322
  1318. package/src/engine/sound/simulation/README.md +277 -277
  1319. package/src/engine/sound/simulation/REFLECTION_IR_PLAN.md +261 -261
  1320. package/src/engine/sound/simulation/VISIBILITY_GRAPH_PLAN.md +121 -121
  1321. package/src/engine/sound/simulation/configureAcousticSimulation.js +48 -48
  1322. package/src/engine/sound/simulation/core/AcousticMedium.js +113 -113
  1323. package/src/engine/sound/simulation/core/AcousticMediumSerializationAdapter.js +27 -27
  1324. package/src/engine/sound/simulation/core/AcousticOccluderIndex.js +312 -312
  1325. package/src/engine/sound/simulation/core/AcousticRegionIndex.js +159 -159
  1326. package/src/engine/sound/simulation/core/AcousticSolution.js +90 -90
  1327. package/src/engine/sound/simulation/core/AcousticSourceState.js +109 -109
  1328. package/src/engine/sound/simulation/core/AcousticVolumeRegion.js +73 -73
  1329. package/src/engine/sound/simulation/core/OcclusionSolver.js +112 -112
  1330. package/src/engine/sound/simulation/core/RayHit.js +50 -50
  1331. package/src/engine/sound/simulation/core/VolumeField.js +149 -149
  1332. package/src/engine/sound/simulation/definition/AcousticDirectivity.js +114 -114
  1333. package/src/engine/sound/simulation/definition/AcousticDirectivitySerializationAdapter.js +30 -30
  1334. package/src/engine/sound/simulation/definition/AcousticMaterial.js +122 -122
  1335. package/src/engine/sound/simulation/definition/AcousticMaterialSerializationAdapter.js +32 -32
  1336. package/src/engine/sound/simulation/ecs/AcousticBody.js +48 -48
  1337. package/src/engine/sound/simulation/ecs/AcousticBodySerializationAdapter.js +34 -34
  1338. package/src/engine/sound/simulation/ecs/AcousticProbeFieldSystem.js +64 -64
  1339. package/src/engine/sound/simulation/ecs/AcousticSimulationSystem.js +75 -75
  1340. package/src/engine/sound/simulation/ecs/AcousticVolume.js +74 -74
  1341. package/src/engine/sound/simulation/ecs/AcousticVolumeSerializationAdapter.js +37 -37
  1342. package/src/engine/sound/simulation/ecs/AcousticVolumeSystem.js +66 -66
  1343. package/src/engine/sound/simulation/probe/AcousticProbeField.js +578 -578
  1344. package/src/engine/sound/simulation/probe/AcousticProbeFieldSerializationAdapter.js +60 -60
  1345. package/src/engine/sound/simulation/probe/ProbeFieldPathingSolution.js +64 -64
  1346. package/src/engine/sound/simulation/probe/ProbeVisibilityRecheck.js +132 -132
  1347. package/src/engine/sound/simulation/probe/acoustic_probe_transfer.js +103 -103
  1348. package/src/engine/sound/simulation/probe/acoustic_trace_energy.js +136 -136
  1349. package/src/engine/sound/simulation/probe/bakeProbeField.js +169 -169
  1350. package/src/engine/sound/simulation/probe/bakeProbeReflectors.js +110 -110
  1351. package/src/engine/sound/simulation/probe/bakeReverbBands.js +110 -110
  1352. package/src/engine/sound/simulation/probe/bakeVisibility.js +148 -148
  1353. package/src/engine/sound/simulation/probe/echogram_band_rt60.js +83 -83
  1354. package/src/engine/sound/simulation/probe/probe_delaunay_edges.js +215 -215
  1355. package/src/engine/sound/simulation/probe/probe_densify_portals.js +140 -140
  1356. package/src/engine/sound/simulation/probe/probe_detect_portals.js +196 -196
  1357. package/src/engine/sound/simulation/probe/probe_edge_key.js +38 -38
  1358. package/src/engine/sound/simulation/probe/probe_edge_visible.js +41 -41
  1359. package/src/engine/sound/simulation/probe/probe_place_sdf_cover.js +82 -82
  1360. package/src/engine/sound/simulation/probe/probe_refine_connectivity.js +181 -181
  1361. package/src/engine/sound/simulation/probe/probe_sdf_grid.js +171 -171
  1362. package/src/engine/sound/simulation/probe/probe_visibility_augment.js +146 -146
  1363. package/src/engine/sound/simulation/probe/probe_visibility_bridge_components.js +167 -167
  1364. package/src/engine/sound/simulation/render/FoaProbeReverbRenderer.js +354 -354
  1365. package/src/engine/sound/simulation/render/ProbeReverbRenderer.js +231 -231
  1366. package/src/engine/sound/simulation/render/foaReverbImpulseResponse.js +105 -105
  1367. package/src/engine/sound/simulation/render/reverbImpulseResponse.js +66 -66
  1368. package/src/engine/sound/simulation/render/reverb_band_tails.js +93 -93
  1369. package/src/engine/sound/simulation/serialization/populateAcousticSerializationRegistry.js +25 -25
  1370. package/src/engine/sound/sopra/README.md +643 -643
  1371. package/src/engine/sound/sopra/SopraEngine.js +432 -432
  1372. package/src/engine/sound/sopra/asset/AssetManagerBufferProvider.js +71 -71
  1373. package/src/engine/sound/sopra/asset/BufferProvider.js +29 -29
  1374. package/src/engine/sound/sopra/asset/StubBufferProvider.js +58 -58
  1375. package/src/engine/sound/sopra/definition/BusDefinition.js +142 -142
  1376. package/src/engine/sound/sopra/definition/BusDefinitionSerializationAdapter.js +54 -54
  1377. package/src/engine/sound/sopra/definition/DuckingRule.js +106 -106
  1378. package/src/engine/sound/sopra/definition/DuckingRuleSerializationAdapter.js +31 -31
  1379. package/src/engine/sound/sopra/definition/EventDescription.js +262 -262
  1380. package/src/engine/sound/sopra/definition/EventDescriptionSerializationAdapter.js +55 -55
  1381. package/src/engine/sound/sopra/definition/MixerSnapshot.js +83 -83
  1382. package/src/engine/sound/sopra/definition/MixerSnapshotSerializationAdapter.js +39 -39
  1383. package/src/engine/sound/sopra/definition/ParameterDefinition.js +117 -117
  1384. package/src/engine/sound/sopra/definition/ParameterDefinitionSerializationAdapter.js +31 -31
  1385. package/src/engine/sound/sopra/definition/VoiceStealMode.js +18 -18
  1386. package/src/engine/sound/sopra/definition/clip/AbstractAudioClip.js +109 -109
  1387. package/src/engine/sound/sopra/definition/clip/BlendContainerAudioClip.js +185 -185
  1388. package/src/engine/sound/sopra/definition/clip/BlendContainerAudioClipSerializationAdapter.js +59 -59
  1389. package/src/engine/sound/sopra/definition/clip/ContainerAudioClip.js +128 -128
  1390. package/src/engine/sound/sopra/definition/clip/RandomContainerAudioClip.js +226 -226
  1391. package/src/engine/sound/sopra/definition/clip/RandomContainerAudioClipSerializationAdapter.js +54 -54
  1392. package/src/engine/sound/sopra/definition/clip/SampleAudioClip.js +191 -191
  1393. package/src/engine/sound/sopra/definition/clip/SampleAudioClipSerializationAdapter.js +39 -39
  1394. package/src/engine/sound/sopra/definition/clip/SequenceContainerAudioClip.js +79 -79
  1395. package/src/engine/sound/sopra/definition/clip/SequenceContainerAudioClipSerializationAdapter.js +42 -42
  1396. package/src/engine/sound/sopra/definition/clip/SilenceAudioClip.js +77 -77
  1397. package/src/engine/sound/sopra/definition/clip/SilenceAudioClipSerializationAdapter.js +27 -27
  1398. package/src/engine/sound/sopra/definition/clip/SwitchContainerAudioClip.d.ts +5 -0
  1399. package/src/engine/sound/sopra/definition/clip/SwitchContainerAudioClip.d.ts.map +1 -1
  1400. package/src/engine/sound/sopra/definition/clip/SwitchContainerAudioClip.js +125 -125
  1401. package/src/engine/sound/sopra/definition/clip/SwitchContainerAudioClipSerializationAdapter.js +41 -41
  1402. package/src/engine/sound/sopra/definition/effect/AbstractAudioEffect.js +24 -24
  1403. package/src/engine/sound/sopra/definition/effect/CompressorEffect.js +120 -120
  1404. package/src/engine/sound/sopra/definition/effect/CompressorEffectSerializationAdapter.js +31 -31
  1405. package/src/engine/sound/sopra/definition/effect/EqEffect.js +128 -128
  1406. package/src/engine/sound/sopra/definition/effect/EqEffectSerializationAdapter.js +29 -29
  1407. package/src/engine/sound/sopra/definition/effect/ReverbEffect.js +113 -113
  1408. package/src/engine/sound/sopra/definition/effect/ReverbEffectSerializationAdapter.js +25 -25
  1409. package/src/engine/sound/sopra/runtime/BusGraph.js +231 -231
  1410. package/src/engine/sound/sopra/runtime/ParameterStore.js +98 -98
  1411. package/src/engine/sound/sopra/runtime/SopraPlaybackContext.js +68 -68
  1412. package/src/engine/sound/sopra/runtime/Voice.js +145 -145
  1413. package/src/engine/sound/sopra/runtime/VoiceManager.js +136 -136
  1414. package/src/engine/sound/sopra/runtime/VoicePool.js +17 -17
  1415. package/src/engine/sound/sopra/serialization/populateSopraSerializationRegistry.js +42 -42
  1416. package/src/engine/sound/sopra/serialization/sopraJSON.js +99 -99
  1417. package/src/engine/sound/sopra/serialization/sopraSerializationHarness.js +49 -49
  1418. package/src/engine/sound/sopra/util/MockAudioContext.js +224 -224
  1419. package/src/engine/sound/sopra/util/fadeOutAndStop.js +60 -60
  1420. package/src/engine/ui/DraggableAspect.js +115 -115
  1421. package/src/engine/ui/apply_pointer_tilt_3d.js +50 -50
  1422. package/src/generation/COORDINATES.md +54 -54
  1423. package/src/generation/GridGenerationResource.js +24 -24
  1424. package/src/generation/GridTaskGroup.js +214 -214
  1425. package/src/generation/REVIEW_01_ACTION_PLAN.md +628 -628
  1426. package/src/generation/automata/CaveGeneratorCellularAutomata.js +79 -79
  1427. package/src/generation/automata/CellularAutomata.js +22 -22
  1428. package/src/generation/compute_writes_union.js +25 -25
  1429. package/src/generation/filtering/CellFilter.js +117 -117
  1430. package/src/generation/filtering/core/CellFilterBinaryOperation.js +37 -37
  1431. package/src/generation/filtering/core/CellFilterOperationTertiary.js +43 -43
  1432. package/src/generation/filtering/core/CellFilterUnaryOperation.js +29 -29
  1433. package/src/generation/filtering/numeric/process/computeCellFilterSobel.js +36 -36
  1434. package/src/generation/filtering/numeric/process/computeFilterSurfaceNormal.js +42 -42
  1435. package/src/generation/filtering/numeric/util/populateSampler2DFromCellFilter.js +18 -18
  1436. package/src/generation/filtering/numeric/util/write_cell_filter_to_sampler2d.js +47 -47
  1437. package/src/generation/filtering/util/cell_filter_weights_pick.js +54 -54
  1438. package/src/generation/grid/GridData.js +233 -233
  1439. package/src/generation/grid/coords/grid_to_texel.js +10 -10
  1440. package/src/generation/grid/coords/texel_to_grid.js +10 -10
  1441. package/src/generation/grid/generation/GridTaskApplyActionToCells.js +90 -90
  1442. package/src/generation/grid/generation/GridTaskDensityMarkerDistribution.js +388 -388
  1443. package/src/generation/grid/generation/GridTaskExecuteRuleTimes.js +127 -127
  1444. package/src/generation/grid/generation/GridTaskSequence.js +58 -58
  1445. package/src/generation/grid/generation/NoopGridTaskGenerator.js +12 -12
  1446. package/src/generation/grid/generation/discrete/GridTaskActionRuleSet.js +48 -48
  1447. package/src/generation/grid/generation/discrete/GridTaskCellularAutomata.js +146 -146
  1448. package/src/generation/grid/generation/discrete/GridTaskConnectRooms.js +540 -540
  1449. package/src/generation/grid/generation/discrete/layer/GridTaskBuildSourceDistanceMap.js +222 -222
  1450. package/src/generation/grid/generation/grid/GridTaskAddNodesFixed.js +44 -44
  1451. package/src/generation/grid/generation/road/GridTaskGenerateRoads.js +614 -614
  1452. package/src/generation/markers/GridCellActionPlaceMarker.js +244 -244
  1453. package/src/generation/markers/GridCellActionPlaceMarkerGroup.js +86 -86
  1454. package/src/generation/markers/actions/terrain/MarkerNodeActionPaintTerrain.js +266 -266
  1455. package/src/generation/markers/emitter/MarkerNodeEmitterGridCellAction.js +49 -49
  1456. package/src/generation/placement/GridCellActionTransformNearbyMarkers.js +120 -120
  1457. package/src/generation/placement/action/GridCellAction.js +45 -45
  1458. package/src/generation/placement/action/GridCellActionPlaceTags.js +140 -140
  1459. package/src/generation/placement/action/GridCellActionWriteFilterToLayer.js +89 -89
  1460. package/src/generation/placement/action/random/CellActionSelectRandom.js +69 -69
  1461. package/src/generation/placement/action/random/weighted/CellActionSelectWeightedRandom.js +91 -91
  1462. package/src/generation/placement/action/util/GridCellActionSequence.js +61 -61
  1463. package/src/generation/placement/action/util/GridCellDisplacedAction.js +61 -61
  1464. package/src/generation/rules/cell/CellMatcherRemovalPreservesConnectivity.js +183 -183
  1465. package/src/generation/theme/ThemeEngine.js +666 -666
  1466. package/src/shade/RENDERER_CONTRACT.md +568 -568
  1467. package/src/shade/descriptor/texture/format/gpu_texture_format_info.js +267 -267
  1468. package/src/shade/device/ShadeDeviceFailure.js +104 -104
  1469. package/src/shade/device/ShadeDeviceFailureReason.js +41 -41
  1470. package/src/shade/device/mock/IMPLEMENTATION_PLAN.md +255 -255
  1471. package/src/shade/device/mock/REVIEW_2026_08_22.md +527 -527
  1472. package/src/shade/device/mock/SoftwareGPUBindGroup.js +23 -23
  1473. package/src/shade/device/mock/SoftwareGPUBindGroupLayout.js +22 -22
  1474. package/src/shade/device/mock/SoftwareGPUError.js +63 -63
  1475. package/src/shade/device/mock/SoftwareGPUPipeline.js +75 -75
  1476. package/src/shade/device/mock/SoftwareGPUQuerySet.js +113 -113
  1477. package/src/shade/device/mock/SoftwareGPUSampler.js +30 -30
  1478. package/src/shade/device/mock/SoftwareGPUShaderModule.js +32 -32
  1479. package/src/shade/device/mock/SoftwareGPUTextureView.js +22 -22
  1480. package/src/shade/device/mock/SoftwareGPUUncapturedErrorEvent.js +23 -23
  1481. package/src/shade/device/mock/normalize_dynamic_offsets.js +26 -26
  1482. package/src/shade/device/mock/texture_copy.js +287 -287
  1483. package/src/shade/device/timing/GPUTimer.js +285 -285
  1484. package/src/shade/device/timing/GPUTimerArray.js +205 -205
  1485. package/src/shade/playground/add_random_lights.js +49 -49
  1486. package/src/shade/playground/add_random_meshes.js +83 -83
  1487. package/src/shade/playground/animation_prototype.js +508 -508
  1488. package/src/shade/playground/animation_prototype_skinning.js +647 -647
  1489. package/src/shade/playground/animation_prototype_stress.js +765 -765
  1490. package/src/shade/playground/enable_picker.js +59 -59
  1491. package/src/shade/playground/gltf_skinned_prototype.js +560 -560
  1492. package/src/shade/playground/gltf_skinned_stress_prototype.js +387 -387
  1493. package/src/shade/playground/gltf_viewer.js +282 -282
  1494. package/src/shade/playground/load_default_environment.js +19 -19
  1495. package/src/shade/playground/main.js +2727 -2727
  1496. package/src/shade/playground/make_animated_instances.js +117 -117
  1497. package/src/shade/playground/prototype_virtual_texture.js +274 -274
  1498. package/src/shade/playground/prototype_volumetrics_01.js +206 -206
  1499. package/src/shade/playground/sample.js +234 -234
  1500. package/src/shade/playground/sample_load_gltf.js +102 -102
  1501. package/src/shade/playground/spread_node3d_grid.js +19 -19
  1502. package/src/shade/renderer/Renderer.js +2436 -2436
  1503. package/src/shade/renderer/ShadeIndirectLightingMode.js +14 -14
  1504. package/src/shade/renderer/animation/ShadeAnimationChannel.js +414 -414
  1505. package/src/shade/renderer/animation/compute_skinned_mesh_clip_bounding_sphere.js +531 -531
  1506. package/src/shade/renderer/animation/pose/PosePlayback.js +55 -55
  1507. package/src/shade/renderer/animation/pose/pose_evaluate_local.js +179 -179
  1508. package/src/shade/renderer/animation/pose/pose_evaluate_world.js +76 -76
  1509. package/src/shade/renderer/animation/skinning/GPUMeshSkinningContext.js +1024 -1024
  1510. package/src/shade/renderer/animation/skinning/SKINNING_BINDING_STRUCT.js +79 -79
  1511. package/src/shade/renderer/animation/skinning/chunk_skin_blend_mesh_local.js +56 -56
  1512. package/src/shade/renderer/binding/BindGroupManager.js +282 -282
  1513. package/src/shade/renderer/binding/adapt_cache_capacity.js +25 -25
  1514. package/src/shade/renderer/buffer/GPUBufferAllocator.js +116 -116
  1515. package/src/shade/renderer/buffer/buffer_pool_ops.js +123 -123
  1516. package/src/shade/renderer/buffer/table/GPU_DATABASE_PAGE_SIZE.js +9 -9
  1517. package/src/shade/renderer/buffer/table/GPU_DATABASE_UPLOAD_BUFFER_LIMIT.js +8 -8
  1518. package/src/shade/renderer/buffer/table/PageBufferPool.js +71 -71
  1519. package/src/shade/renderer/buffer/table/PageSlotAllocator.js +95 -95
  1520. package/src/shade/renderer/camera/Camera.js +281 -281
  1521. package/src/shade/renderer/camera/GPUCameraContext.js +233 -233
  1522. package/src/shade/renderer/camera/orbital/README.md +84 -84
  1523. package/src/shade/renderer/deferred/shader_composite_volumetrics.js +75 -75
  1524. package/src/shade/renderer/dynamic/DYNAMIC_MESH_VERTEX.js +25 -25
  1525. package/src/shade/renderer/dynamic/GPUDynamicMeshRenderer.js +342 -342
  1526. package/src/shade/renderer/dynamic/GPUDynamicMeshResidency.js +167 -167
  1527. package/src/shade/renderer/dynamic/shader_dynamic_mesh.js +170 -170
  1528. package/src/shade/renderer/extension/FrameContext.js +263 -263
  1529. package/src/shade/renderer/extension/FramePhase.js +104 -104
  1530. package/src/shade/renderer/extension/FrameRecord.js +126 -126
  1531. package/src/shade/renderer/extension/GBufferTextures.js +83 -83
  1532. package/src/shade/renderer/extension/PresentTarget.js +34 -34
  1533. package/src/shade/renderer/extension/RENDER_EXTENSION_DESIGN.md +918 -918
  1534. package/src/shade/renderer/extension/RenderExtension.js +69 -69
  1535. package/src/shade/renderer/extension/RenderExtensionRegistry.js +319 -319
  1536. package/src/shade/renderer/extension/SceneColor.js +40 -40
  1537. package/src/shade/renderer/extension/ViewTextures.js +78 -78
  1538. package/src/shade/renderer/fow/GPUFogOfWarRenderer.js +182 -182
  1539. package/src/shade/renderer/geometry/Geometry.js +608 -608
  1540. package/src/shade/renderer/geometry/geometry_build_from_meshlet_geometry.js +342 -342
  1541. package/src/shade/renderer/geometry/meshlet_geometry_transfer.js +41 -41
  1542. package/src/shade/renderer/geometry/primitives/make_box_geometry.js +153 -153
  1543. package/src/shade/renderer/geometry/primitives/make_cylinder_geometry.js +240 -240
  1544. package/src/shade/renderer/geometry/primitives/make_plane_geometry.js +145 -145
  1545. package/src/shade/renderer/geometry/primitives/make_polyhedron_geometry.js +327 -327
  1546. package/src/shade/renderer/geometry/primitives/make_torus_geometry.js +107 -107
  1547. package/src/shade/renderer/geometry/primitives/make_torus_knot_geometry.js +165 -165
  1548. package/src/shade/renderer/geometry/virtual/VGEO_FORMAT.md +607 -607
  1549. package/src/shade/renderer/geometry/virtual/build/VGeoBuildOptions.js +66 -66
  1550. package/src/shade/renderer/geometry/virtual/build/VGeoBuildResult.js +32 -32
  1551. package/src/shade/renderer/geometry/virtual/build/VGeoFaceGraph.js +103 -103
  1552. package/src/shade/renderer/geometry/virtual/build/VGeoGatheredFaces.js +25 -25
  1553. package/src/shade/renderer/geometry/virtual/build/VGeoLevel.js +33 -33
  1554. package/src/shade/renderer/geometry/virtual/build/VGeoLevelOptions.js +62 -62
  1555. package/src/shade/renderer/geometry/virtual/build/VGeoLevelSummary.js +49 -49
  1556. package/src/shade/renderer/geometry/virtual/build/VGeoSubmesh.js +20 -20
  1557. package/src/shade/renderer/geometry/virtual/build/VirtualGeometryMesh.js +214 -214
  1558. package/src/shade/renderer/geometry/virtual/build/gltf/GltfByteSource.js +122 -122
  1559. package/src/shade/renderer/geometry/virtual/build/gltf/GltfCollectReport.js +29 -29
  1560. package/src/shade/renderer/geometry/virtual/build/gltf/GltfDocument.js +210 -210
  1561. package/src/shade/renderer/geometry/virtual/build/gltf/GltfGeometry.js +48 -48
  1562. package/src/shade/renderer/geometry/virtual/build/gltf/GltfGeometryInstance.js +25 -25
  1563. package/src/shade/renderer/geometry/virtual/build/gltf/GltfGeometrySelection.js +28 -28
  1564. package/src/shade/renderer/geometry/virtual/build/gltf/gltf_collect_geometries.js +296 -296
  1565. package/src/shade/renderer/geometry/virtual/build/gltf/gltf_open_document.js +166 -166
  1566. package/src/shade/renderer/geometry/virtual/build/gltf/gltf_read_geometry.js +199 -199
  1567. package/src/shade/renderer/geometry/virtual/build/vgeo_assemble_pages.js +173 -173
  1568. package/src/shade/renderer/geometry/virtual/build/vgeo_bounding_sphere.js +28 -28
  1569. package/src/shade/renderer/geometry/virtual/build/vgeo_build.js +200 -200
  1570. package/src/shade/renderer/geometry/virtual/build/vgeo_build_levels.js +1786 -1786
  1571. package/src/shade/renderer/geometry/virtual/build/vgeo_build_page_nodes.js +157 -157
  1572. package/src/shade/renderer/geometry/virtual/build/vgeo_build_root_group.js +26 -26
  1573. package/src/shade/renderer/geometry/virtual/build/vgeo_compute_vertex_normals.js +75 -75
  1574. package/src/shade/renderer/geometry/virtual/build/vgeo_compute_vertex_tangents.js +143 -143
  1575. package/src/shade/renderer/geometry/virtual/build/vgeo_containing_sphere.js +44 -44
  1576. package/src/shade/renderer/geometry/virtual/build/vgeo_extract_submesh.js +41 -41
  1577. package/src/shade/renderer/geometry/virtual/build/vgeo_gather_faces.js +71 -71
  1578. package/src/shade/renderer/geometry/virtual/build/vgeo_graph_components.js +123 -123
  1579. package/src/shade/renderer/geometry/virtual/build/vgeo_narrow_indices.js +26 -26
  1580. package/src/shade/renderer/geometry/virtual/build/vgeo_normalize_source.js +112 -112
  1581. package/src/shade/renderer/geometry/virtual/build/vgeo_partition_graph.js +56 -56
  1582. package/src/shade/renderer/geometry/virtual/build/vgeo_sphere_inflate_to_contain.js +70 -70
  1583. package/src/shade/renderer/geometry/virtual/build/vgeo_split_face_set.js +187 -187
  1584. package/src/shade/renderer/geometry/virtual/build/vgeo_split_face_set_packed.js +207 -207
  1585. package/src/shade/renderer/geometry/virtual/format/VGEO_DEFECT.js +18 -18
  1586. package/src/shade/renderer/geometry/virtual/format/VGeoDefect.js +38 -38
  1587. package/src/shade/renderer/geometry/virtual/format/VGeoWriteOptions.js +64 -64
  1588. package/src/shade/renderer/geometry/virtual/format/attribute/VGEO_ATTRIBUTE.js +13 -13
  1589. package/src/shade/renderer/geometry/virtual/format/attribute/VGEO_ATTRIBUTES.js +50 -50
  1590. package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_attribute_allocate.js +28 -28
  1591. package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_attribute_mask.js +23 -23
  1592. package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_attributes_read.js +31 -31
  1593. package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_layers_copy_vertex.js +32 -32
  1594. package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_layers_declare.js +46 -46
  1595. package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_layers_like.js +19 -19
  1596. package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_layers_test_fixtures.js +27 -27
  1597. package/src/shade/renderer/geometry/virtual/format/checksum/vgeo_asset_id_preimage.js +62 -62
  1598. package/src/shade/renderer/geometry/virtual/format/checksum/vgeo_compute_asset_id.js +13 -13
  1599. package/src/shade/renderer/geometry/virtual/format/checksum/vgeo_compute_directory_block_checksum.js +41 -41
  1600. package/src/shade/renderer/geometry/virtual/format/checksum/vgeo_compute_frame_checksum.js +41 -41
  1601. package/src/shade/renderer/geometry/virtual/format/checksum/vgeo_compute_header_checksum.js +25 -25
  1602. package/src/shade/renderer/geometry/virtual/format/cluster/VGEO_CLUSTER_OFFSET.js +20 -20
  1603. package/src/shade/renderer/geometry/virtual/format/cluster/VGEO_CLUSTER_RECORD_SIZE.js +5 -5
  1604. package/src/shade/renderer/geometry/virtual/format/cluster/VGEO_MAX_CHILD_REF.js +5 -5
  1605. package/src/shade/renderer/geometry/virtual/format/cluster/VGEO_MAX_CLUSTER_TRIANGLE_COUNT.js +5 -5
  1606. package/src/shade/renderer/geometry/virtual/format/cluster/VGEO_MAX_CLUSTER_VERTEX_COUNT.js +5 -5
  1607. package/src/shade/renderer/geometry/virtual/format/cluster/VGEO_NO_CHILD.js +5 -5
  1608. package/src/shade/renderer/geometry/virtual/format/cluster/VGEO_NO_GROUP.js +5 -5
  1609. package/src/shade/renderer/geometry/virtual/format/cluster/VGEO_SAME_PAGE.js +6 -6
  1610. package/src/shade/renderer/geometry/virtual/format/dag/VirtualGeometryCluster.js +81 -81
  1611. package/src/shade/renderer/geometry/virtual/format/dag/VirtualGeometryGroup.js +37 -37
  1612. package/src/shade/renderer/geometry/virtual/format/dag/VirtualGeometryPageNode.js +55 -55
  1613. package/src/shade/renderer/geometry/virtual/format/directory/VGEO_DIRECTORY_BLOCK_RECORDS.js +5 -5
  1614. package/src/shade/renderer/geometry/virtual/format/directory/VGEO_DIRECTORY_FLAG_ROOT_PAGE.js +5 -5
  1615. package/src/shade/renderer/geometry/virtual/format/directory/VGEO_DIRECTORY_RECORD_SIZE.js +5 -5
  1616. package/src/shade/renderer/geometry/virtual/format/directory/vgeo_directory_block_count.js +12 -12
  1617. package/src/shade/renderer/geometry/virtual/format/frame/VGEO_CHILD_OFFSET.js +11 -11
  1618. package/src/shade/renderer/geometry/virtual/format/frame/VGEO_CHILD_TABLE_ENTRY_SIZE.js +5 -5
  1619. package/src/shade/renderer/geometry/virtual/format/frame/VGEO_CODEC_LZ4.js +11 -11
  1620. package/src/shade/renderer/geometry/virtual/format/frame/VGEO_CODEC_RAW.js +5 -5
  1621. package/src/shade/renderer/geometry/virtual/format/frame/VGEO_FRAME_ALIGNMENT.js +5 -5
  1622. package/src/shade/renderer/geometry/virtual/format/frame/VGEO_FRAME_HEADER_SIZE.js +5 -5
  1623. package/src/shade/renderer/geometry/virtual/format/frame/VGEO_FRAME_OFFSET.js +13 -13
  1624. package/src/shade/renderer/geometry/virtual/format/frame/VGEO_PAGE_MAGIC.js +5 -5
  1625. package/src/shade/renderer/geometry/virtual/format/frame/VGeoFrameBlob.js +21 -21
  1626. package/src/shade/renderer/geometry/virtual/format/frame/VGeoFrameExtent.js +59 -59
  1627. package/src/shade/renderer/geometry/virtual/format/frame/vgeo_codec_name.js +27 -27
  1628. package/src/shade/renderer/geometry/virtual/format/frame/vgeo_decode_frame_blob.js +84 -84
  1629. package/src/shade/renderer/geometry/virtual/format/frame/vgeo_encode_frame_blob.js +56 -56
  1630. package/src/shade/renderer/geometry/virtual/format/frame/vgeo_frame_decode_bound.js +35 -35
  1631. package/src/shade/renderer/geometry/virtual/format/frame/vgeo_scan_frames.js +74 -74
  1632. package/src/shade/renderer/geometry/virtual/format/header/VGEO_FORMAT_VERSION.js +6 -6
  1633. package/src/shade/renderer/geometry/virtual/format/header/VGEO_HASH_ALGO_XXH3.js +5 -5
  1634. package/src/shade/renderer/geometry/virtual/format/header/VGEO_HEADER_OFFSET.js +31 -31
  1635. package/src/shade/renderer/geometry/virtual/format/header/VGEO_HEADER_SIZE.js +5 -5
  1636. package/src/shade/renderer/geometry/virtual/format/header/VGEO_LEVEL_OFFSET.js +20 -20
  1637. package/src/shade/renderer/geometry/virtual/format/header/VGEO_LEVEL_RECORD_SIZE.js +9 -9
  1638. package/src/shade/renderer/geometry/virtual/format/header/VGEO_MAGIC.js +5 -5
  1639. package/src/shade/renderer/geometry/virtual/format/header/VGEO_MIN_READER_VERSION.js +5 -5
  1640. package/src/shade/renderer/geometry/virtual/format/header/VGEO_PAYLOAD_ENCODING_SHADE_MESHLET_V1.js +5 -5
  1641. package/src/shade/renderer/geometry/virtual/format/header/VGeoLevelRecord.js +41 -41
  1642. package/src/shade/renderer/geometry/virtual/format/page/VGEO_GROUP_RECORD_SIZE.js +5 -5
  1643. package/src/shade/renderer/geometry/virtual/format/page/VGEO_MAX_GROUP_CLUSTER_COUNT.js +6 -6
  1644. package/src/shade/renderer/geometry/virtual/format/page/VGEO_METRIC_OFFSET.js +21 -21
  1645. package/src/shade/renderer/geometry/virtual/format/page/VGEO_METRIC_SIZE.js +5 -5
  1646. package/src/shade/renderer/geometry/virtual/format/page/VGEO_NODE_FANOUT.js +9 -9
  1647. package/src/shade/renderer/geometry/virtual/format/page/VGEO_NODE_FLAG_LEAF.js +5 -5
  1648. package/src/shade/renderer/geometry/virtual/format/page/VGEO_NODE_OFFSET.js +10 -10
  1649. package/src/shade/renderer/geometry/virtual/format/page/VGEO_NODE_RECORD_SIZE.js +5 -5
  1650. package/src/shade/renderer/geometry/virtual/format/page/VGEO_PAGE_HEADER_SIZE.js +5 -5
  1651. package/src/shade/renderer/geometry/virtual/format/page/VGEO_PAGE_OFFSET.js +16 -16
  1652. package/src/shade/renderer/geometry/virtual/format/payload/VGEO_ENCODING_FLAGS_MASK.js +6 -6
  1653. package/src/shade/renderer/geometry/virtual/format/payload/VGeoEncodedPayload.js +18 -18
  1654. package/src/shade/renderer/geometry/virtual/format/payload/vgeo_encode_cluster_payload.js +200 -200
  1655. package/src/shade/renderer/geometry/virtual/format/payload/vgeo_payload_attribute_offset.js +39 -39
  1656. package/src/shade/renderer/geometry/virtual/format/payload/vgeo_payload_size.js +20 -20
  1657. package/src/shade/renderer/geometry/virtual/format/prototypeVGEOFormatViewer.js +2063 -2063
  1658. package/src/shade/renderer/geometry/virtual/format/vgeo_validate_container.js +1622 -1622
  1659. package/src/shade/renderer/geometry/virtual/format/vgeo_write_container.js +485 -485
  1660. package/src/shade/renderer/global_illumination/brick4/cpu/brick4_empty_gpu_structure.d.ts +23 -0
  1661. package/src/shade/renderer/global_illumination/brick4/cpu/brick4_empty_gpu_structure.d.ts.map +1 -0
  1662. package/src/shade/renderer/global_illumination/brick4/cpu/brick4_empty_gpu_structure.js +34 -0
  1663. package/src/shade/renderer/gpu_primitive/sort/ffx/graph_radix_sort.js +115 -115
  1664. package/src/shade/renderer/gpu_primitive/sort/one_sweep/verify_radix_sort_one_sweep.js +110 -110
  1665. package/src/shade/renderer/light/environment/octahedral_resample_kernel.js +50 -50
  1666. package/src/shade/renderer/light/environment/octahedral_uv_to_direction.js +30 -30
  1667. package/src/shade/renderer/light/environment/resample_cube_to_octahedral.js +157 -157
  1668. package/src/shade/renderer/light/make_sunlight.js +35 -35
  1669. package/src/shade/renderer/light/model/io/light_record_point.js +39 -39
  1670. package/src/shade/renderer/loader/gltf/GLTF_PRIMITIVE_MODE.js +23 -23
  1671. package/src/shade/renderer/loader/gltf/MESHOPT_COMPRESSION_PLAN.md +793 -793
  1672. package/src/shade/renderer/loader/gltf/ext/GltfBufferViewExtension.js +54 -54
  1673. package/src/shade/renderer/loader/gltf/ext/GltfBufferViewExtensionSet.js +109 -109
  1674. package/src/shade/renderer/loader/gltf/ext/gltf_apply_buffer_view_extensions.js +69 -69
  1675. package/src/shade/renderer/loader/gltf/ext/gltf_buffer_view_extensions.js +22 -22
  1676. package/src/shade/renderer/loader/gltf/ext/gltf_geometry_extensions.js +39 -39
  1677. package/src/shade/renderer/loader/gltf/ext/meshopt/MeshoptBufferViewExtension.js +301 -301
  1678. package/src/shade/renderer/loader/gltf/gltf_component_type.js +223 -223
  1679. package/src/shade/renderer/loader/gltf/gltf_node_world_matrices.js +137 -137
  1680. package/src/shade/renderer/loader/gltf/gltf_parse_container.js +241 -241
  1681. package/src/shade/renderer/loader/gltf/gltf_read_accessor.js +294 -294
  1682. package/src/shade/renderer/loader/gltf/gltf_used_image_indices.js +49 -49
  1683. package/src/shade/renderer/loader/gltf/load_gltf.js +1253 -1253
  1684. package/src/shade/renderer/loader/gltf/tiny-gltf.js +646 -646
  1685. package/src/shade/renderer/loader/usd/UsdError.js +33 -33
  1686. package/src/shade/renderer/loader/usd/UsdExtensionRegistry.js +69 -69
  1687. package/src/shade/renderer/loader/usd/load_usd.js +226 -226
  1688. package/src/shade/renderer/loader/usd/parse_usda.js +931 -931
  1689. package/src/shade/renderer/loader/usd/unpack_usdz.js +106 -106
  1690. package/src/shade/renderer/loader/usd/usd_build_scene.js +658 -658
  1691. package/src/shade/renderer/loader/usd/usd_compose_transform.js +258 -258
  1692. package/src/shade/renderer/loader/usd/usd_decode_image.js +384 -384
  1693. package/src/shade/renderer/loader/usd/usd_lz4.js +182 -182
  1694. package/src/shade/renderer/loader/usd/usd_triangulate.js +159 -159
  1695. package/src/shade/renderer/material/ShadeMaterial.d.ts +2 -2
  1696. package/src/shade/renderer/material/ShadeMaterial.js +2 -2
  1697. package/src/shade/renderer/material/standard/fragment_gbuffer.js +334 -334
  1698. package/src/shade/renderer/material/standard/fragment_gbuffer_vt.js +169 -169
  1699. package/src/shade/renderer/material/standard/material_gbuffer_vertex.js +45 -45
  1700. package/src/shade/renderer/material/standard_material_to_struct.js +23 -23
  1701. package/src/shade/renderer/particles/prototypeParticleSystem.js +450 -450
  1702. package/src/shade/renderer/particles/runtime/spawn_request_encoding.js +52 -52
  1703. package/src/shade/renderer/pool/TransientResourcePool.js +392 -392
  1704. package/src/shade/renderer/postprocess/bloom/bloom_chain_resolution.js +29 -29
  1705. package/src/shade/renderer/postprocess/bloom/graph_bloom_downsample.js +109 -109
  1706. package/src/shade/renderer/postprocess/bloom/graph_postprocess_bloom.js +155 -155
  1707. package/src/shade/renderer/postprocess/cas/shader_ffx_rcas.js +114 -114
  1708. package/src/shade/renderer/postprocess/dof/DOF_AUTOFOCUS_PARAMS_STRUCT.js +25 -25
  1709. package/src/shade/renderer/postprocess/dof/DOF_FOCUS_STATE_STRUCT.js +23 -23
  1710. package/src/shade/renderer/postprocess/dof/DOF_PRESETS.js +27 -27
  1711. package/src/shade/renderer/postprocess/dof/chunk_dof_coc_band_diameter.js +41 -41
  1712. package/src/shade/renderer/postprocess/dof/chunk_dof_coc_band_radius.js +32 -32
  1713. package/src/shade/renderer/postprocess/dof/chunk_dof_coc_coefficient.js +26 -26
  1714. package/src/shade/renderer/postprocess/dof/compute_dof_autofocus_params.js +64 -64
  1715. package/src/shade/renderer/postprocess/dof/compute_dof_uniform.js +54 -54
  1716. package/src/shade/renderer/postprocess/dof/gather/DOF2_CONFIG.js +91 -91
  1717. package/src/shade/renderer/postprocess/dof/gather/DOF2_PRESETS.js +9 -9
  1718. package/src/shade/renderer/postprocess/dof/gather/DOF2_SETTINGS_STRUCT.js +66 -66
  1719. package/src/shade/renderer/postprocess/dof/gather/DOF2_TEXTURE_FORMAT.js +22 -22
  1720. package/src/shade/renderer/postprocess/dof/gather/DepthOfFieldU.js +292 -292
  1721. package/src/shade/renderer/postprocess/dof/gather/chunk_dof2_coc_to_near_far.js +25 -25
  1722. package/src/shade/renderer/postprocess/dof/gather/chunk_dof2_gather_weight.js +30 -30
  1723. package/src/shade/renderer/postprocess/dof/gather/compute_dof2_uniform.js +54 -54
  1724. package/src/shade/renderer/postprocess/dof/gather/graph_postprocess_dof2.js +194 -194
  1725. package/src/shade/renderer/postprocess/dof/gather/shader_dof2_fill.js +68 -68
  1726. package/src/shade/renderer/postprocess/dof/gather/shader_dof2_gather_far.js +140 -140
  1727. package/src/shade/renderer/postprocess/dof/gather/shader_dof2_gather_near.js +142 -142
  1728. package/src/shade/renderer/postprocess/dof/gather/shader_dof2_recombine.js +99 -99
  1729. package/src/shade/renderer/postprocess/dof/gather/shader_dof2_setup.js +102 -102
  1730. package/src/shade/renderer/postprocess/dof/gather/shader_dof2_tile_dilate.js +75 -75
  1731. package/src/shade/renderer/postprocess/dof/gather/shader_dof2_tile_max.js +73 -73
  1732. package/src/shade/renderer/postprocess/dof/raymarch/DOF_SETTINGS_STRUCT.js +49 -49
  1733. package/src/shade/renderer/postprocess/dof/raymarch/DepthOfField.js +298 -298
  1734. package/src/shade/renderer/postprocess/dof/raymarch/graph_postprocess_dof.js +244 -244
  1735. package/src/shade/renderer/postprocess/dof/raymarch/shader_dof_composite.js +91 -91
  1736. package/src/shade/renderer/postprocess/dof/raymarch/shader_dof_firefly_cleanup.js +61 -61
  1737. package/src/shade/renderer/postprocess/dof/raymarch/shader_dof_prefilter.js +90 -90
  1738. package/src/shade/renderer/postprocess/dof/raymarch/shader_dof_raymarch_compute.js +267 -267
  1739. package/src/shade/renderer/postprocess/dof/raymarch/shader_dof_slice_compute.js +271 -271
  1740. package/src/shade/renderer/postprocess/dof/raymarch/shader_dof_tile_dilate.js +70 -70
  1741. package/src/shade/renderer/postprocess/dof/raymarch/shader_dof_tile_max.js +72 -72
  1742. package/src/shade/renderer/postprocess/dof/shader_dof_autofocus.js +99 -99
  1743. package/src/shade/renderer/postprocess/gtao/generate_magic_square_noise.js +51 -51
  1744. package/src/shade/renderer/postprocess/motion_blur/MotionBlur.js +56 -56
  1745. package/src/shade/renderer/postprocess/upscale-dither/NOTES.md +78 -78
  1746. package/src/shade/renderer/postprocess/upscale-dither/UpscaleDither.js +95 -95
  1747. package/src/shade/renderer/postprocess/upscale-dither/fragment_shader_upscale_dither.js +204 -204
  1748. package/src/shade/renderer/rasterize/bucket/construct_primitive_state.js +58 -58
  1749. package/src/shade/renderer/rasterize/fast/graph_rasterize_scene_fast.js +230 -230
  1750. package/src/shade/renderer/rasterize/native/graph_rasterize_meshes_alpha_tested.js +278 -278
  1751. package/src/shade/renderer/rasterize/native/oit/chunk_material_side_should_draw.js +26 -26
  1752. package/src/shade/renderer/rasterize/native/oit/shader_oit_resolve_moments_ibl.js +178 -178
  1753. package/src/shade/renderer/rasterize/standard/graph_rasterize_partial_opaque.js +235 -235
  1754. package/src/shade/renderer/rasterize/standard/graph_rasterize_remaining_opaque.js +208 -208
  1755. package/src/shade/renderer/rasterize/standard/graph_rasterize_scene.js +206 -206
  1756. package/src/shade/renderer/scene/DynamicMesh.js +70 -70
  1757. package/src/shade/renderer/scene/DynamicMeshBatch.js +90 -90
  1758. package/src/shade/renderer/scene/GPUSceneContext.js +901 -901
  1759. package/src/shade/renderer/scene/SceneNodeBinding.js +56 -56
  1760. package/src/shade/renderer/scene/SkinnedMesh.js +55 -55
  1761. package/src/shade/renderer/scene/TransformAuthority.js +33 -33
  1762. package/src/shade/renderer/scene/optimization/deduplicate_geometries.js +38 -38
  1763. package/src/shade/renderer/scene/optimization/deduplicate_images.js +93 -93
  1764. package/src/shade/renderer/scene/optimization/geometry/internal/anchor_grow_topological.d.ts.map +1 -1
  1765. package/src/shade/renderer/scene/optimization/geometry/internal/anchor_grow_topological.js +12 -3
  1766. package/src/shade/renderer/scene/optimization/geometry/internal/match_refit_validate.js +127 -127
  1767. package/src/shade/renderer/scene/optimization/geometry/internal/match_solve_transform.js +492 -492
  1768. package/src/shade/renderer/scene/optimization/geometry/internal/match_validate_topology.js +3 -3
  1769. package/src/shade/renderer/scene/optimization/geometry/internal/seed_correspondence_from_window.js +3 -3
  1770. package/src/shade/renderer/scene/optimization/optimize_texture_set.js +81 -81
  1771. package/src/shade/renderer/scene/optimization/source_to_sampler2d.js +21 -21
  1772. package/src/shade/renderer/scene/serialization/STANDARD_MATERIAL_SERIALIZED_BYTE_SIZE.js +12 -12
  1773. package/src/shade/renderer/scene/serialization/deserialize_scene.d.ts.map +1 -1
  1774. package/src/shade/renderer/scene/serialization/deserialize_scene.js +0 -2
  1775. package/src/shade/renderer/shader/chunk/geometry/quaternion/dual/chunk_dual_quat_transform_point.js +26 -26
  1776. package/src/shade/renderer/shader/chunk/noise/chunk_interleaved_gradient_noise.js +20 -20
  1777. package/src/shade/renderer/shader/chunk/spherical_harmonics/dering/chunk_sh2_dering_optimize_positive.js +61 -61
  1778. package/src/shade/renderer/shader/chunk/texture/chunk_texture_filter_fxaa_lite.js +225 -225
  1779. package/src/shade/renderer/shader/chunk/texture/dither/chunk_dither_color_8bit_gradient_noise.js +25 -25
  1780. package/src/shade/renderer/shadow/map/DirectionalLightShadowmap.js +384 -384
  1781. package/src/shade/renderer/shadow/map/PointLightShadowmap.js +105 -105
  1782. package/src/shade/renderer/shadow/map/SpotLightShadowmap.js +63 -63
  1783. package/src/shade/renderer/shadow/map/TetrahedronPointLightShadowmap.js +89 -89
  1784. package/src/shade/renderer/shadow/map/shadowmap_update_score.js +232 -232
  1785. package/src/shade/renderer/texture/GPUTextureAllocator.js +85 -85
  1786. package/src/shade/renderer/texture/texture_pool_ops.js +171 -171
  1787. package/src/shade/renderer/texture/virtual/PageTableQuadAllocator.js +253 -253
  1788. package/src/shade/renderer/texture/virtual/README.md +276 -276
  1789. package/src/shade/renderer/texture/virtual/VTPageTable.js +330 -330
  1790. package/src/shade/renderer/texture/virtual/VTPhysicalCache.js +349 -349
  1791. package/src/shade/renderer/texture/virtual/VT_CONSTANTS.js +154 -154
  1792. package/src/shade/renderer/texture/virtual/VT_STACK_INFO_STRUCT.js +20 -20
  1793. package/src/shade/renderer/texture/virtual/VirtualTextureManager.js +1276 -1276
  1794. package/src/shade/renderer/texture/virtual/VirtualTextureStack.js +106 -106
  1795. package/src/shade/renderer/texture/virtual/build_chunk_vt_lookup.js +150 -150
  1796. package/src/shade/renderer/texture/virtual/build_shader_vt_feedback.js +232 -232
  1797. package/src/shade/renderer/texture/virtual/chunk_vt_request_key.js +28 -28
  1798. package/src/shade/renderer/texture/virtual/source/VTSourceImage.js +207 -207
  1799. package/src/shade/renderer/texture/virtual/source/VTSourceProcedural.js +135 -135
  1800. package/src/shade/renderer/texture/virtual/source/VTSourceTiled.js +132 -132
  1801. package/src/shade/renderer/water/GPUWaterRenderer.js +167 -167
  1802. package/src/shade/renderer/water/shader_water.js +246 -246
  1803. package/src/shade/renderer/water/water_height_texture_from_sampler.js +56 -56
  1804. package/src/shade/vitest.setup.mjs +89 -89
  1805. package/src/shade/wgsl/validate_wgsl_source.js +316 -316
  1806. package/src/view/controller/controls/NumericIntervalControl.js +90 -90
  1807. package/src/view/controller/controls/Vector2Control.js +83 -83
  1808. package/src/view/controller/controls/Vector3Control.js +90 -90
  1809. package/src/view/elements/Group.js +56 -56
  1810. package/src/view/elements/drawCheckers.js +33 -33
  1811. package/src/view/elements/windrose/WindRoseDiagram.js +369 -369
  1812. package/src/view/graphics/WebGLContextFailureView.js +76 -76
  1813. package/src/view/layout/DockPanel.js +51 -51
  1814. package/src/view/layout/SplitView.js +365 -365
  1815. package/src/view/layout/show_popup_menu.js +81 -81
  1816. package/src/view/minimap/MinimapCanvasView.js +411 -411
  1817. package/src/view/minimap/MinimapImageCache.js +88 -88
  1818. package/src/view/minimap/MinimapMarkerCollection.d.ts +0 -1
  1819. package/src/view/minimap/MinimapMarkerCollection.d.ts.map +1 -1
  1820. package/src/view/minimap/MinimapMarkerCollection.js +180 -180
  1821. package/src/view/minimap/dom/MinimapCameraView.js +206 -206
  1822. package/src/view/minimap/minimap_focus_area.js +97 -97
  1823. package/editor/process/symbolic/SocketsSymbolicDisplay.js +0 -192
  1824. package/src/core/geom/3d/topology/struct/binary/io/moved_tests.txt +0 -37
  1825. package/src/engine/ecs/attachment/Attachment.d.ts +0 -40
  1826. package/src/engine/ecs/attachment/Attachment.d.ts.map +0 -1
  1827. package/src/engine/ecs/attachment/Attachment.js +0 -57
  1828. package/src/engine/ecs/attachment/AttachmentBinding.d.ts +0 -28
  1829. package/src/engine/ecs/attachment/AttachmentBinding.d.ts.map +0 -1
  1830. package/src/engine/ecs/attachment/AttachmentBinding.js +0 -41
  1831. package/src/engine/ecs/attachment/AttachmentSystem.d.ts +0 -61
  1832. package/src/engine/ecs/attachment/AttachmentSystem.d.ts.map +0 -1
  1833. package/src/engine/ecs/attachment/AttachmentSystem.js +0 -307
  1834. package/src/engine/ecs/attachment/BoneAttachmentBinding.d.ts +0 -27
  1835. package/src/engine/ecs/attachment/BoneAttachmentBinding.d.ts.map +0 -1
  1836. package/src/engine/ecs/attachment/BoneAttachmentBinding.js +0 -69
  1837. package/src/engine/ecs/attachment/TransformAttachmentBinding.d.ts +0 -4
  1838. package/src/engine/ecs/attachment/TransformAttachmentBinding.d.ts.map +0 -1
  1839. package/src/engine/ecs/attachment/TransformAttachmentBinding.js +0 -51
  1840. package/src/engine/ecs/sockets/AttachmentSocket.d.ts +0 -32
  1841. package/src/engine/ecs/sockets/AttachmentSocket.d.ts.map +0 -1
  1842. package/src/engine/ecs/sockets/AttachmentSocket.js +0 -60
  1843. package/src/engine/ecs/sockets/AttachmentSocketType.d.ts +0 -6
  1844. package/src/engine/ecs/sockets/AttachmentSocketType.d.ts.map +0 -1
  1845. package/src/engine/ecs/sockets/AttachmentSocketType.js +0 -8
  1846. package/src/engine/ecs/sockets/AttachmentSockets.d.ts +0 -57
  1847. package/src/engine/ecs/sockets/AttachmentSockets.d.ts.map +0 -1
  1848. package/src/engine/ecs/sockets/AttachmentSockets.js +0 -130
  1849. package/src/engine/ecs/sockets/BoneAttachmentSocket.d.ts +0 -29
  1850. package/src/engine/ecs/sockets/BoneAttachmentSocket.d.ts.map +0 -1
  1851. package/src/engine/ecs/sockets/BoneAttachmentSocket.js +0 -56
  1852. package/src/engine/ecs/sockets/serialization/AttachmentSocketsAssetLoader.d.ts +0 -6
  1853. package/src/engine/ecs/sockets/serialization/AttachmentSocketsAssetLoader.d.ts.map +0 -1
  1854. package/src/engine/ecs/sockets/serialization/AttachmentSocketsAssetLoader.js +0 -19
  1855. package/src/shade/renderer/animation/_dump_resolve.d.mts +0 -2
  1856. package/src/shade/renderer/animation/_dump_resolve.d.mts.map +0 -1
  1857. package/src/shade/renderer/animation/_dump_resolve.mjs +0 -5
  1858. package/src/shade/renderer/scene/optimization/geometry/internal/bt_face_loop_helpers.d.ts +0 -32
  1859. package/src/shade/renderer/scene/optimization/geometry/internal/bt_face_loop_helpers.d.ts.map +0 -1
  1860. package/src/shade/renderer/scene/optimization/geometry/internal/bt_face_loop_helpers.js +0 -77
  1861. package/src/shade/renderer/scene/optimization/geometry/internal/bt_find_edge_between.d.ts +0 -12
  1862. package/src/shade/renderer/scene/optimization/geometry/internal/bt_find_edge_between.d.ts.map +0 -1
  1863. package/src/shade/renderer/scene/optimization/geometry/internal/bt_find_edge_between.js +0 -35
  1864. package/src/shade/renderer/scene/serialization/linear_to_pq.d.ts +0 -2
  1865. package/src/shade/renderer/scene/serialization/linear_to_pq.d.ts.map +0 -1
  1866. package/src/shade/renderer/scene/serialization/linear_to_pq.js +0 -14
  1867. package/src/shade/renderer/scene/serialization/pq_to_linear.d.ts +0 -9
  1868. package/src/shade/renderer/scene/serialization/pq_to_linear.d.ts.map +0 -1
  1869. package/src/shade/renderer/scene/serialization/pq_to_linear.js +0 -30
@@ -1,1523 +1,1523 @@
1
- # Input System Mapping — design
2
-
3
- A binding layer for keyboard, pointer buttons and gamepad, replacing `Input`/`InputSystem`, the keyboard half
4
- of `InputController`/`InputControllerSystem`, `EditorKeyMap` and `Tool.handleKeyboardEvent`.
5
-
6
- Status: **design, not yet implemented.** The only file in this directory today is
7
- [`InputBinding.js`](InputBinding.js) (the `{path, event}` record used by
8
- [`Input`](../components/Input.js)); it is superseded by §4.
9
-
10
- > **Revision 3.** Rewritten after an adversarial review of revision 2 found three defects that made it
11
- > unimplementable, and several false claims about the existing codebase. What changed, and why, is in §17.
12
- > The three that matter: the sequence matcher's reset paths were broken (§5); occlusion could not reach polled
13
- > reads, so a modal dialog could not stop a camera (§8); and coordinate bindings had no home in either the
14
- > component or the wire format (§9.1, §10). Two scope decisions were taken at the same time: **pointer
15
- > gestures are out of scope** (§1.1), and **`SimpleStateMachine` is not used** (§5.6).
16
-
17
- ---
18
-
19
- ## 0. Summary
20
-
21
- Two decisions carry the design.
22
-
23
- > **1. Every input this system binds is either a switch or a coordinate.** `InputDeviceSwitch` already exists
24
- > and is already what keyboard keys, pointer buttons and gamepad buttons *are*. A coordinate is an
25
- > N-dimensional analog channel — stick deflection, wheel ticks, pointer position.
26
- >
27
- > **2. Every trigger is a predicate over (held switches + its own state), and a binding fires on that
28
- > predicate's rising edge** — evaluated the instant an edge arrives, never on a frame boundary.
29
-
30
- | Requirement | Falls out of |
31
- |---|---|
32
- | Combinations (chords) | A chord is the AND of its children's predicates |
33
- | Sequences | A sequence is a predicate over a small linear matcher, advanced per edge (§5) |
34
- | Cross-device combos | A chord cannot tell which device a child came from — there is nothing to make work |
35
- | Arbitrary devices | A device is anything that hands out switches and coordinates (§3) |
36
- | Layers, top wins | Cascade over an action table, compiled once per change, like CSS (§7) |
37
- | Serializable bindings | A binding is `(trigger, action name)`; both halves are plain data (§6.1, §10) |
38
- | No added latency | Evaluation is eager on the edge; the tick services only time (§2) |
39
-
40
- Two defects in the current systems that this fixes on the way past:
41
-
42
- 1. **Binding resolution can throw.** `resolvePath` throws on an unresolvable path
43
- ([`resolvePathByArray.js:47`](../../../../core/json/resolvePathByArray.js)), which is why a gamepad binding
44
- is a load-order landmine (see [`GAMEPAD_API_PROPOSAL.md §3`](../../GAMEPAD_API_PROPOSAL.md)). Source
45
- resolution here is total.
46
- 2. **Nothing can express a combination.** `Ctrl+Z` is not expressible as a path, which is why
47
- [`EditorKeyMap`](../../../../../editor/EditorKeyMap.js) exists as a parallel, unserializable, unrebindable
48
- input system made of `if (event.ctrlKey)`.
49
-
50
- ---
51
-
52
- ## 1. Scope
53
-
54
- ### 1.1 Pointer gestures are out of scope
55
-
56
- `PointerDevice` synthesizes gestures on top of its buttons — `tap`, `drag`, `dragStart`, `dragEnd`, `move`,
57
- `wheel`, `pinch`, `pinchStart`, `pinchEnd` — and those are **not** modelled here. They are stateful
58
- constructions with their own parameters (`tap` fires on *release*, gated on ≤ 10 px of travel and ≤ 1 s;
59
- `PointerDevice.js:20-40`), not switches, and folding them into the trigger algebra is a separate piece of work.
60
-
61
- This is a deliberate limit with a measurable cost. Of the pointer bindings in shipping consumers:
62
-
63
- | `down`/`up` | `tap` | `drag`/`dragStart`/`dragEnd` | `move` | `wheel` | `pinch`/`pinchStart` |
64
- |---|---|---|---|---|---|
65
- | 6 | 4 | 4 | 2 | 2 | 2 |
66
-
67
- Only the first column is expressible. **Consumers bound to gestures stay on `InputController`**, which is
68
- therefore *reduced*, not retired, by this program (§14). Chief among them:
69
- `StrategyInteractionController` (17 bindings, including both pinch bindings),
70
- `makeOrbitalCameraController` (tap + down + drag + wheel), and `RadialContextMenu`.
71
-
72
- ### 1.2 What is replaced
73
-
74
- | Mechanism | Disposition | Why |
75
- |---|---|---|
76
- | [`Input`](../components/Input.js) + [`InputSystem`](../systems/InputSystem.js) | **Retired** (one holdout, §14) | Magic string paths; one source per binding; no combinations; `resolvePath` throws; `serializable = false` |
77
- | [`EditorKeyMap`](../../../../../editor/EditorKeyMap.js) | **Retired** | Exists only because chords are inexpressible above; hard-coded, unrebindable, undiscoverable |
78
- | `Tool.handleKeyboardEvent` | **Retired** | An unconditional forward to the active tool, with no gating |
79
- | [`KeyboardCameraController`](../controllers/KeyboardCameraController.js) | **Retired** | 16 `bind()` calls maintaining a `controls` struct — a 2-D coordinate written by hand (§3.3) |
80
- | [`InputController`](../components/InputController.js) + [`InputControllerSystem`](../systems/InputControllerSystem.js) | **Reduced** | Keyboard and button bindings move; gesture bindings stay (§1.1) |
81
- | Direct `engine.devices.*.on.*.add(...)` in gameplay code | **Case by case** | Not bindable, not rebindable, not occludable; but gesture subscriptions have nowhere to go yet |
82
-
83
- **Not in scope at all:** direct device reads in `prototype*.js` / `test*.js` harnesses. Those are internal
84
- tools, and polling `pad.buttons[GamepadButtons.a].is_down` remains the blessed path for anything that does not
85
- want a binding layer. The ISM sits *above* the devices; it does not close them off.
86
-
87
- ---
88
-
89
- ## 2. Evaluation: eager on edges, ticked only for time
90
-
91
- There is **no input buffer and no added frame of latency.** An edge is evaluated and dispatched inside the
92
- device signal that produced it.
93
-
94
- ```
95
- InputDeviceSwitch.down / .up (keyboard/pointer: DOM event. gamepad: poll.)
96
- |
97
- v
98
- ISM edge handler --- synchronous, no queue ---------------------------------
99
- |
100
- | 1. stamp arrival time
101
- | 2. update #held : BitSet
102
- | 3. append to the recorder, if one is attached (§2.4)
103
- | 4. walk maps by `order` descending, skipping suppressed maps (§8):
104
- | for each candidate trigger touching this switch,
105
- | in specificity order (§4.6):
106
- | was := trigger.satisfied
107
- | now := trigger.test(#held)
108
- | trigger.satisfied := now
109
- | if (now && !was) consume, dispatch
110
- | 5. apply changes deferred during dispatch (§2.3)
111
- v
112
- dataset.sendEvent(entity, action, event) -- consumer runs here, this instant
113
- ```
114
-
115
- `#held` is exact at dispatch time by construction, because dispatch *is* the edge. No replay, no
116
- reconstruction.
117
-
118
- ### 2.1 The tick services time, and only time
119
-
120
- `System.update(dt)` dispatches no edge-driven action. It services the four things that change without an edge:
121
-
122
- 1. **`InputTriggerHold` maturity.** A hold for 0.5 s becomes true 0.5 s after its child did, which no edge
123
- announces. When it matures it emits a **synthetic edge** into the same walk as step 4 above, carrying the
124
- hold's own `collectSources()` set so consumption (§8) has something to match against. Latency is bounded by
125
- the frame, which is inherent: nothing observes time more finely than the loop observing time.
126
- 2. **Sequence step windows** (§5.2) — both the refresh while a previous step still holds, and the expiry.
127
- 3. **`Delta` coordinate reset** (§3.3).
128
- 4. **Suppression recompute** when the linked map set, `order` or `occlusion` changed during the frame (§8).
129
-
130
- A trigger built only from switches and chords is untouched by `update`.
131
-
132
- ### 2.2 Polled devices are bounded by their own poll rate
133
-
134
- `GamepadDevice` polls once per animation frame and drives its switches from the result
135
- (`GamepadDevice.js:370-407`), so gamepad edges cannot be finer-grained than that — a Gamepad API property, not
136
- one this design imposes. `GamepadDevice.poll()` is callable manually, so a consumer driving a fixed-rate loop
137
- can raise its own resolution. Keyboard and pointer edges are DOM-driven and carry no such floor.
138
-
139
- ### 2.3 The re-entrancy contract
140
-
141
- Consumer code runs inside a DOM event handler. Four cases, each with a stated rule:
142
-
143
- * **Structural change** — a handler adds or removes an entity, and therefore an `InputMap`. Queued, applied at
144
- step 5, after the current edge finishes.
145
- * **Binding edits on a linked map** — a handler calls `bind()` / `pushLayer()`, which §9.3 says triggers a
146
- recompile. The recompile is queued with structural changes and applied at step 5; the walk always runs to
147
- completion against the program it started with. This path is not exotic: it is the rebinding-capture flow.
148
- * **Nested edges** — a handler calls `GamepadDevice.poll()` (blessed in §2.2) or synchronously dispatches a DOM
149
- event (`element.click()`, `.focus()`), re-entering the walk. **Rejected**: a re-entrant edge is queued and
150
- replayed at step 5 rather than processed inline, because processing it inline would mutate `#held` under the
151
- outer walk and clobber the shared `InputActionEvent` (§6.3) before the outer handler finished reading it.
152
- * **A handler that throws** — it throws. `#held` and the recorder are updated *before* dispatch (steps 2–3), so
153
- ISM state stays consistent and the failure is visible, per the codebase's position on not swallowing errors.
154
-
155
- *(For contrast, `InputControllerSystem` carries a `deferred` array for the first case that **nothing ever
156
- populates** — `add()` registers directly under a `//TODO handle case where binding is added mid-dispatch`
157
- (`InputControllerSystem.js:119-121`). The hazard is real and currently unhandled, not handled badly.)*
158
-
159
- ### 2.4 Ordering, and what it costs
160
-
161
- Action effects land wherever in the frame the edge arrived, so two actions in one frame are ordered by
162
- arrival, not by system order. That is the direct consequence of wanting zero latency, and §4.6 and §5.5 record
163
- the two places it is observable.
164
-
165
- ### 2.5 Recording and replay
166
-
167
- An optional recorder captures, per frame: the frame's `dt`, every switch edge as
168
- `(source_index, is_down, time)`, and a sample of every **coordinate channel** any linked map references.
169
- Coordinates must be recorded because they are read by polling, not driven by edges — without them,
170
- `InputActionEvent.value` and every `axis*` read diverge on replay. `dt` must be recorded because hold maturity
171
- and window expiry are evaluated on the tick, so a different frame cadence produces different fire times.
172
-
173
- Replay injects recorded edges at their recorded positions within each recorded frame and drives the same eager
174
- path. Interned `source_index` values are stable only within one link generation, so the recorder writes the
175
- source table alongside and remaps on load.
176
-
177
- The recorder is an observer, not the mechanism — attach it for a determinism run or a bug report, leave it off
178
- otherwise.
179
-
180
- ### 2.6 `preventDefault`, and its granularity
181
-
182
- [`KeyboardDevice`](../../devices/KeyboardDevice.js) calls `event.preventDefault()` when the pressed key's
183
- `down` signal *has handlers* (`KeyboardDevice.js:112-118`). Since the ISM subscribes to precisely the keys some
184
- live binding references, browser-default suppression tracks the bound key set automatically.
185
-
186
- **Granularity is per key, not per chord**, and this is worth knowing before it surprises someone: binding only
187
- `Ctrl+S` subscribes `s`'s `down` signal, so a *bare* `S` keydown is also `preventDefault`ed anywhere the device
188
- element sees it. This is inherited behaviour, not new — `InputController` binding `keyboard/keys/s/down` does
189
- the same — but chords make it newly reachable. Changing it means changing `KeyboardDevice`, which is out of
190
- scope here.
191
-
192
- ---
193
-
194
- ## 3. Devices, switches and coordinates
195
-
196
- ### 3.1 The adapter
197
-
198
- ```js
199
- /**
200
- * Adapts a concrete input device to the ISM.
201
- *
202
- * Resolution must be TOTAL: an adapter answers for every code it declares, whether or not hardware is
203
- * present. Absent hardware reads as "up" and as zero, never as undefined, and never throws. This is the
204
- * property `resolvePath` lacked.
205
- */
206
- export class InputDeviceAdapter {
207
- /** Stable identifier, written into serialized bindings. ASCII, lower_snake_case. @type {string} */
208
- id = "";
209
-
210
- /** @param {number} code @returns {InputDeviceSwitch} */
211
- getSwitch(code) { }
212
-
213
- /** @param {number[]|Float64Array} result @param {number} offset @param {number} code */
214
- readCoordinate(result, offset, code) { }
215
-
216
- /** Begin/stop observing the device. Adapters with nothing to subscribe to leave these alone. */
217
- startup() { }
218
- shutdown() { }
219
-
220
- /** Publish this frame's relative-channel totals and start a new window. See the latch rule in §3.3. */
221
- latchDeltas() { }
222
- }
223
- ```
224
-
225
- A channel's *dimensionality* is not asked of the adapter. It is a static property of the channel code, known
226
- to the typed constructors (§3.3), so a binding on an unregistered device still knows its own shape.
227
-
228
- | Adapter | `id` | Switch codes | Coordinate codes |
229
- |---|---|---|---|
230
- | `KeyboardInputDeviceAdapter` | `"keyboard"` | [`KeyCodes`](../../devices/KeyCodes.js) | — |
231
- | `PointerInputDeviceAdapter` | `"pointer"` | `MouseButtons` | `PointerCoordinates` |
232
- | `GamepadInputDeviceAdapter` | `"gamepad"` | [`GamepadButtons`](../../devices/gamepad/GamepadButtons.js) | `GamepadCoordinates` |
233
-
234
- Three enums are new, and each needs a stability note because their values become `uint16` codes on disk:
235
-
236
- * **`MouseButtons`** — `left: 0, middle: 1, right: 2, back: 3, forward: 4`. These are
237
- **`MouseEvent.button` ordinals**, matching how `PointerDevice.buttons[]` is indexed
238
- (`PointerDevice.js:300-318`: `mouseButtonLeft` is `[0]`, `mouseButtonMiddle` is `[1]`, `mouseButtonRight` is
239
- `[2]`). They are deliberately **not** the `MouseEvent.buttons` *bitmask* order, which is left/right/middle —
240
- taking that order would index `buttons[1]` for a right-click and fire every right-click binding on the
241
- middle button, into saved data.
242
- * **`PointerCoordinates`** — `position`, `move`, `wheel`.
243
- * **`GamepadCoordinates`** — `left_stick`, `right_stick`, `dpad`.
244
-
245
- Values are append-only once shipped, like `KeyCodes` and `GamepadButtons` before them.
246
-
247
- A fourth device — MIDI pedal, wheel, eye tracker, a network-driven replay source — is an adapter and a
248
- registration call. Nothing in the trigger, layer, cascade or serialization layers changes, because none of them
249
- can tell devices apart.
250
-
251
- The gamepad adapter binds `GamepadDevice.main`, the persistent handle: it exists before any hardware does,
252
- survives disconnection, and rebinds to the oldest connected pad on a swap (`GamepadDevice.js:110, 306-344`). A
253
- serialized gamepad binding therefore resolves at load time and keeps working across a hot-swap — the defect
254
- `GAMEPAD_API_PROPOSAL.md §3` identified and could not fix from the device side alone.
255
-
256
- **A device that is not registered** is not an error. A map naming an unregistered `id` links successfully and
257
- its bindings on that device are inert — the same contract as connected-but-absent hardware, so a saved map
258
- never fails to load because a peripheral is missing. `registerDevice()` may be called after maps are linked; it
259
- recompiles every linked map (§9.3), at which point those bindings become live.
260
-
261
- ### 3.2 Switch sources
262
-
263
- ```js
264
- export class InputSource {
265
- device = ""; // InputDeviceAdapter#id
266
- code = 0;
267
- }
268
- ```
269
-
270
- One flat value type with `equals`/`hash`/`toBinaryBuffer`/`fromBinaryBuffer`, deliberately *not* a
271
- `KeySource`/`GamepadButtonSource`/… hierarchy: a chord over `{keyboard:ctrl, pointer:0}` needs no polymorphic
272
- dispatch, serialization is `(device_index: Uint8, code: Uint16)` against a per-map device table (§10) with no
273
- type tags, and interning to an integer (§9.3) is a two-field hash after which the hot path never touches the
274
- object again. The typing that matters to an author lives on the *trigger* constructors (§4.2).
275
-
276
- ### 3.3 Coordinates — analog channels of arbitrary dimensionality
277
-
278
- An analog channel is not a scalar axis. `WheelEvent` carries `deltaX`, `deltaY` and `deltaZ`, and
279
- [`PointerDevice`](../../devices/PointerDevice.js) surfaces all three (`PointerDevice.js:398-404`). A 1-D axis
280
- abstraction would have to either drop `deltaZ` or invent three channels for one control.
281
-
282
- *(Precisely: `PointerDevice` reports `sign(event.deltaX/Y/Z)`, so components are -1, 0 or 1 and scroll
283
- magnitude is discarded at the device. A `Delta` wheel channel therefore accumulates **tick counts**, not
284
- distance. That is the device's existing behaviour; the ISM reports what it is given.)*
285
-
286
- ```js
287
- export class InputCoordinateSource {
288
- device = "";
289
- code = 0;
290
- /** Component count. 2 for a stick or pointer position, 3 for a wheel. @type {number} */
291
- dimensions = 1;
292
- /** @type {InputCoordinateKind} */
293
- kind = InputCoordinateKind.Normalized;
294
- }
295
-
296
- export const InputCoordinateKind = {
297
- /** A position in some space. Read at any time, no accumulation. Pointer position. */
298
- Absolute: 0,
299
- /** A change. Accumulates within a frame, zeroed at the frame boundary. Wheel. */
300
- Delta: 1,
301
- /** Deflection, each component in [-1, 1]. Stick, d-pad. */
302
- Normalized: 2
303
- };
304
- ```
305
-
306
- `kind` is the verb — what the numbers *mean*. `Delta` is the one with behaviour attached, and it is why the
307
- tick exists at all: two wheel events in one frame must sum.
308
-
309
- **The `Delta` visibility rule, stated exactly.** A relative channel **latches**; it is not cleared. The
310
- adapter accumulates arriving events into one buffer, and once per frame `update` publishes that buffer into a
311
- second one and starts a fresh accumulation window. Readers see the published value.
312
-
313
- Latching rather than clearing is load-bearing, and the obvious alternative is wrong. Device events arrive
314
- *between* frames, so an adapter that zeroed its accumulator at the top of a frame would destroy exactly the
315
- events that frame exists to report. Latching also makes the value **independent of system execution order**:
316
- every reader in a frame sees the same number whether it runs before or after the ISM, which is what the
317
- "same value for every reader" guarantee actually requires.
318
-
319
- An **eager handler** reading a `Delta` channel mid-frame sees the value published for the frame in progress —
320
- the same one the tick-time readers will see — not a running total of events that have arrived since. Those
321
- belong to the next frame.
322
-
323
- **Reading.** The general accessor writes into an indexable numeric buffer; common dimensionalities get typed
324
- conveniences over the engine's vector types:
325
-
326
- ```js
327
- map.coordinate(result, offset, 'look'); // Float64Array | number[] - any dimensionality
328
- map.axis1(v1, 'throttle'); // Vector1
329
- map.axis2(v2, 'move'); // Vector2
330
- map.axis3(v3, 'wheel'); // Vector3
331
- ```
332
-
333
- Result-first argument order follows the engine's `f(result, ...)` convention.
334
-
335
- **Channels.** A coordinate action is driven by a channel, and the consumer cannot tell which kind:
336
-
337
- ```js
338
- InputCoordinateGamepad.from(GamepadCoordinates.left_stick) // hardware channel
339
- InputCoordinatePointer.from(PointerCoordinates.wheel) // hardware channel, 3-D
340
- InputCoordinateSwitches.from([ // N-D from 2N switch triggers
341
- InputTriggerKey.from('a'), InputTriggerKey.from('d'), // x: negative, positive
342
- InputTriggerKey.from('w'), InputTriggerKey.from('s') // y: negative, positive
343
- ])
344
- InputCoordinateCompose.from(x_channel, y_channel) // stitch channels
345
- ```
346
-
347
- `InputCoordinateSwitches` is [`KeyboardCameraController`](../controllers/KeyboardCameraController.js) as data,
348
- and it generalises past 2-D for free.
349
-
350
- **Shaping stays at the call site.** No dead zone, no response curve, no sensitivity inside the ISM. It returns
351
- the composed raw value and the consumer calls
352
- [`apply_radial_dead_zone`](../../analog/apply_radial_dead_zone.js). This is the position
353
- `GAMEPAD_API_PROPOSAL.md §1.2` argues at length; reversing it here would put the same policy back in the same
354
- wrong layer, one floor up.
355
-
356
- ### 3.4 Derived switches: a direction is a coordinate with a threshold
357
-
358
- `InputTriggerDirection` turns a coordinate into a switch — "the stick is in the *down-right* octant". This is
359
- what makes motion inputs work (§5.4), and it is not bolted on for them: a d-pad is already a coordinate in this
360
- codebase (`gamepad_write_dpad_vector`), so one path serves stick and d-pad.
361
-
362
- The eight directional switches derived from one coordinate are **mutually exclusive by construction**, and the
363
- derived layer **releases the old direction before pressing the new one**, so exclusivity is never momentarily
364
- violated. §5.4 depends on both.
365
-
366
- Three properties an implementer must know, because none is free:
367
-
368
- * **Threshold.** Producing a boolean from a coordinate requires one by definition. This is not an exception to
369
- §3.3, which forbids shaping the *analog value* handed to a consumer. Constructor argument, defaulted.
370
- * **Sampling.** Octant transitions are observed only as often as the channel updates. On a gamepad that is the
371
- rAF poll (§2.2), so a fast quarter-circle can pass from `Down` to `Right` between two polls without ever
372
- reporting `DownRight`, and a motion requiring that step silently fails. Sequences over directions are
373
- reliable at hand speed, not at frame-perfect speed.
374
- * **Hysteresis.** A stick resting on an octant boundary chatters release/press pairs. The threshold carries an
375
- angular hysteresis band, defaulted, for the same reason it carries a magnitude threshold: without it a
376
- boundary rest position resets hold timers continuously.
377
-
378
- ---
379
-
380
- ## 4. Triggers
381
-
382
- ### 4.1 One interface, two methods and one field
383
-
384
- ```js
385
- export class InputTrigger {
386
- /**
387
- * Is this trigger's condition currently met?
388
- * Pure for leaves and combinators. `InputTriggerHold` and `InputTriggerSequence` additionally consult
389
- * per-instance state, which edges and ticks maintain.
390
- *
391
- * @param {BitSet} held interned switch indices currently down
392
- * @returns {boolean}
393
- */
394
- test(held) { }
395
-
396
- /**
397
- * Every switch index this trigger can be affected by. Collected once, at compile time (§9.3).
398
- * @param {number[]} result
399
- */
400
- collectSources(result) { }
401
-
402
- /**
403
- * Last observed value of `test`, per compiled instance. A binding fires when `test` is true and this
404
- * is false. Initialised at compile time to `test(held-at-compile)` - NOT to false. See §4.5.
405
- * @type {boolean}
406
- */
407
- satisfied = false;
408
- }
409
- ```
410
-
411
- **A binding fires on the rising edge of `test`.** That single rule covers press, release, hold, chord, sequence
412
- and direction, and it is why there is no `Started`/`Performed`/`Canceled` phase enum and no `edge` field.
413
-
414
- ### 4.2 Leaf triggers — typed constructors
415
-
416
- ```js
417
- InputTriggerKey.from('space')
418
- InputTriggerKey.from(KeyCodes.space)
419
- InputTriggerMouseButton.from(MouseButtons.left)
420
- InputTriggerGamepadButton.from(GamepadButtons.a)
421
- InputTriggerDirection.from(stick, InputDirection.DownRight) // stick: any InputCoordinate* channel
422
- ```
423
-
424
- Each extends `InputTriggerSwitch`, which holds one `InputSource` and whose `test` is `held.get(index)`. The
425
- subclasses add no fields — they are typed, validating constructors that fix the device and check the code
426
- against that device's enum. `InputTriggerKey.from('nonexistent')` fails at authoring time under `assert.*`,
427
- which is compiled out of production, so the typing is free in shipped code.
428
-
429
- Serialization round-trips the flat form, so a deserialized trigger is an `InputTriggerSwitch`, not an
430
- `InputTriggerKey`. Nothing needs the subclass identity at runtime — it exists for the author and the assertion.
431
-
432
- ### 4.3 Combinators
433
-
434
- ```js
435
- InputTriggerChord.from(a, b, c) // test = AND over children
436
- InputTriggerRelease.from(a) // test = NOT child.test
437
- InputTriggerHold.from(a, 0.5) // test = child.test AND continuously true for >= 0.5s
438
- InputTriggerSequence.from(a, b, c) // test = the matcher completed on this edge (§5)
439
- ```
440
-
441
- Combinators take *triggers*, not sources, which is why the requirements compose:
442
-
443
- ```js
444
- // "the chord ended"
445
- InputTriggerRelease.from(InputTriggerChord.from(InputTriggerKey.from('ctrl'),
446
- InputTriggerKey.from('z')))
447
-
448
- // "hold Ctrl+Shift for half a second"
449
- InputTriggerHold.from(InputTriggerChord.from(InputTriggerKey.from('ctrl'),
450
- InputTriggerKey.from('shift')), 0.5)
451
-
452
- // cross-device: nothing here knows a device is involved
453
- InputTriggerChord.from(InputTriggerKey.from('ctrl'),
454
- InputTriggerMouseButton.from(MouseButtons.left))
455
- ```
456
-
457
- `InputTriggerRelease` as *negation* rather than an edge kind is what makes it compose — an edge flag on a chord
458
- could not express "when this chord stops holding".
459
-
460
- **One composition that does not mean what it looks like:** `InputTriggerRelease.from(sequence)`. A sequence's
461
- `test` is a single-edge pulse (§5.2 step 5), so negating it yields "true except on the completing edge", which
462
- rises on the *next* evaluated edge after every completion — an echo of the fire, not "the sequence stopped
463
- matching". A sequence's partial-match state is deliberately not exposed to `test`. Do not wrap a sequence in
464
- `Release`.
465
-
466
- ### 4.4 Step timeout lives on the sequence
467
-
468
- ```js
469
- InputTriggerSequence.from(a, b, c).withStepTimeout(0.25)
470
- ```
471
-
472
- Default **1.0 s**, which suits editor and menu sequences whose first step is a chord the author must release
473
- before reaching for the next key. Fighting-game motions want 0.15–0.25 s. §5.2 defines exactly what the window
474
- measures, which is not what a naive reading suggests.
475
-
476
- Timing was specified here as **real time**, stamped at edge arrival, on the argument that a paused game must
477
- not give a chord an infinite window and a game at 0.25× speed must not quarter the player's reflexes.
478
-
479
- **Superseded.** As built it is *simulation* time, advanced by `update(dt)` and nowhere else. The reason is
480
- testability: a real-time clock cannot be driven from a spec without faking one, and faking it is a mock. The
481
- consequences are real and accepted — windows stretch under slow motion and freeze while the game is paused,
482
- so a hold inside a pause menu can never mature. See the implementation plan's amendments section for the full
483
- argument.
484
-
485
- ### 4.5 `satisfied` is initialised from state, not from false
486
-
487
- At compile time each trigger instance sets `satisfied := test(held)` against the currently held switches. It is
488
- **not** zero-initialised, and the difference is not academic:
489
- `InputTriggerRelease.from(InputTriggerChord.from(ctrl, z))` has `test` true whenever the chord is *not* held —
490
- which is almost always. Zero-initialised, the first edge touching `ctrl` or `z` would see `now=true, was=false`
491
- and fire "the chord ended" before the chord ever existed.
492
-
493
- This matters on every compile, not just the first: maps link mid-dispatch (§2.3), and `registerDevice` and
494
- binding edits both recompile live maps while keys are held.
495
-
496
- ### 4.6 Precedence within a layer
497
-
498
- Candidate triggers on one edge are ordered, and the order is total so it never depends on declaration accident:
499
-
500
- 1. **Sequences first**, longer before shorter (by step count).
501
- 2. Then by **chord width** — the number of switches that must be simultaneously held. `Hold` and `Release`
502
- rank by their child's width, so `Hold(Chord(a,b), t)` ranks as width 2 and beats `Key(a)`; `Direction` ranks
503
- as width 1.
504
- 3. Then declaration order within the layer.
505
-
506
- A fired trigger consumes its switches for the remainder of the current edge (§8), so `Ctrl+Z` firing means `Z`
507
- does not.
508
-
509
- **The condition on that guarantee.** Consumption is scoped to one edge, so it arbitrates only triggers that
510
- complete on the *same* edge. `Ctrl` pressed before `Z` gives one edge (`z↓`) at which both `Ctrl+Z` and `Z` are
511
- satisfiable, and the chord wins. `Z` pressed **before** `Ctrl` gives two edges: `z↓` fires `Z`'s action (the
512
- chord is not yet satisfiable, so there is nothing to lose to), then `ctrl↓` fires `undo`. **Both fire.**
513
-
514
- This is inherent to zero-latency evaluation — suppressing the first would mean either delaying it or retracting
515
- a dispatched action, and both are excluded. Practically it is rarely reached, because modifier-first is how
516
- people type chords. Where it matters, do not bind a bare key in the same layer as a chord that contains it.
517
-
518
- ---
519
-
520
- ## 5. Sequence semantics
521
-
522
- ### 5.1 State
523
-
524
- Per sequence binding instance, three numbers, held on the **compiled clone** of the trigger (§9.3):
525
-
526
- ```js
527
- #step // steps matched so far, 0..N
528
- #stepSatisfied // last observed value of steps[#step].test
529
- #window // time the current step's window last refreshed
530
- ```
531
-
532
- *(An earlier revision put these in typed arrays on the program, indexed by sequence-binding index. Cloning per
533
- binding is what the compiler does anyway — authored triggers are shared, so evaluation state cannot live on
534
- them — and once each binding owns a private trigger object, three fields on it are the same thing with less
535
- bookkeeping. Nothing allocates per evaluation either way.)*
536
-
537
- Entering a state is one operation, and **every** path into a state uses it. That is the whole of §17's most
538
- important correction:
539
-
540
- ```js
541
- function enter(k, held, now) {
542
- step = k;
543
- satisfied = (k < N) ? steps[k].test(held) : false; // the entry snapshot
544
- window = now;
545
- }
546
- ```
547
-
548
- ### 5.2 The rule
549
-
550
- Evaluated at each edge (device or synthetic) and each tick:
551
-
552
- ```
553
- 1. if (step > 0) {
554
- if (steps[step - 1].test(held)) window := now // previous step still holds
555
- else if (now - window > step_timeout) enter(0, held, now) // window expired
556
- }
557
-
558
- 2. cur := steps[step].test(held)
559
-
560
- 3. if (cur && !satisfied) {
561
- enter(step + 1, held, now)
562
- if (step === N) {
563
- fire() // the sequence's own `test` reads true for this edge
564
- enter(0, held, now)
565
- }
566
- } else {
567
- satisfied := cur
568
- }
569
- ```
570
-
571
- Three properties do all the work:
572
-
573
- * **Advance on a rising edge, not on satisfaction.** Otherwise one long press of `A` satisfies both steps
574
- of `[A, A]`.
575
- * **The entry snapshot, on every entry.** Advance, expiry and post-fire wrap all route through `enter`, so a
576
- reset can never leave a stale `satisfied` behind. `enter` never indexes `steps[N]`.
577
- * **The window measures time since the previous step stopped holding**, not time since the advance. Step 1
578
- refreshes `window` for as long as `steps[step-1]` is still satisfied, which is what makes charge inputs work
579
- (§5.4) and is why the tick must service sequences even when no edge arrives.
580
-
581
- **A mismatch does not reset. Only window expiry resets.** A sequence is a leniency window, and the window is
582
- what §5.2 step 1 defines.
583
-
584
- ### 5.3 Worked: `[A, A, B]` on `A↓ A↑ A↓ A↑ A↓ B↓`
585
-
586
- | edge | held | step | step-1 holds? | `cur` | `satisfied` | result |
587
- |---|---|---|---|---|---|---|
588
- | — | {} | 0 | — | — | false (compile snapshot) | |
589
- | `A↓` | {A} | 0 | — | true | false | **rise** → `enter(1)`, `satisfied := A.test({A}) = true` |
590
- | `A↑` | {} | 1 | no | false | true | `satisfied := false` |
591
- | `A↓` | {A} | 1 | yes → refresh | true | false | **rise** → `enter(2)`, `satisfied := B.test({A}) = false` |
592
- | `A↑` | {} | 2 | no | false | false | — |
593
- | `A↓` | {A} | 2 | yes → refresh | false | false | — |
594
- | `B↓` | {A,B} | 2 | yes → refresh | true | false | **rise** → `enter(3)` = N → **fire**, `enter(0)` |
595
-
596
- No failure function, no KMP: the entry snapshot alone prevents one press from satisfying two steps.
597
-
598
- **Post-fire, the case revision 2 got wrong.** `enter(0, {A,B}, now)` snapshots `satisfied := A.test({A,B}) =
599
- true`. So with `A` still held, tapping `B` again gives `cur = true, satisfied = true` — no rise, no fire.
600
- Revision 2 left `satisfied` untouched on the post-fire reset, and holding `A` while mashing `B` machine-gunned
601
- the action off a single `A` press.
602
-
603
- ### 5.4 Worked: chords, motions and charge
604
-
605
- **`Ctrl+Shift+A > B`** — steps are triggers, and a chord is a trigger, so this is not a special case:
606
-
607
- ```js
608
- map.bind('promote', InputTriggerSequence.from(
609
- InputTriggerChord.from(InputTriggerKey.from('ctrl'),
610
- InputTriggerKey.from('shift'),
611
- InputTriggerKey.from('a')),
612
- InputTriggerKey.from('b')
613
- ));
614
- ```
615
-
616
- `ctrl↓`, `shift↓`: chord false. `a↓`: chord rises → `enter(1)`, `satisfied := b.test({ctrl,shift,a}) = false`.
617
- Then `b↓` fires — **whether or not the modifiers were released first**, because leaf and chord tests are
618
- *subset* tests over the held set, and because holding the chord keeps refreshing the window rather than
619
- starting its clock.
620
-
621
- **Quarter-circle forward, `↓ ↘ →`** — this is the case that only works because directions are derived from a
622
- coordinate (§3.4) rather than read off raw d-pad buttons:
623
-
624
- ```js
625
- const stick = InputCoordinateGamepad.from(GamepadCoordinates.left_stick);
626
-
627
- map.bind('hadouken', InputTriggerSequence.from(
628
- InputTriggerDirection.from(stick, InputDirection.Down),
629
- InputTriggerDirection.from(stick, InputDirection.DownRight),
630
- InputTriggerDirection.from(stick, InputDirection.Right)
631
- ).withStepTimeout(0.2));
632
- ```
633
-
634
- | edge | held | step | `cur` | result |
635
- |---|---|---|---|---|
636
- | `Down↓` | {Down} | 0 | true | **rise** → `enter(1)`, `satisfied := DownRight.test({Down}) = false` |
637
- | `Down↑` | {} | 1 | false | — |
638
- | `DownRight↓` | {DownRight} | 1 | true | **rise** → `enter(2)`, `satisfied := Right.test({DownRight}) = false` |
639
- | `DownRight↑` | {} | 2 | false | — |
640
- | `Right↓` | {Right} | 2 | true | **rise** → `enter(3)` = N → **fire** |
641
-
642
- The two snapshots in the result column are the trick. With raw d-pad buttons `↓` and `→` can be held together,
643
- `Right.test({Down,Right})` is `true` on entry to state 2, and the motion can never complete. Exclusivity by
644
- construction is what removes that failure mode — which is why §12.2 tests it in both directions.
645
-
646
- **Dragon punch, `→ ↓ ↘`** — the same trace with steps permuted; each entry snapshot is false for the same
647
- reason. Verified also against the realistic rolled input `Right, DownRight, Down, DownRight`, where the
648
- intermediate `DownRight↓` at step 1 is a non-advancing mismatch (ignored, correctly) and the final one fires.
649
-
650
- **Charge, `hold ← for 2 s, then →`** — the case revision 2 published as passing and which in fact failed:
651
-
652
- ```js
653
- InputTriggerSequence.from(
654
- InputTriggerHold.from(InputTriggerDirection.from(stick, InputDirection.Left), 2.0),
655
- InputTriggerDirection.from(stick, InputDirection.Right)
656
- )
657
- ```
658
-
659
- At t=2.0 the hold matures, emits a synthetic edge (§2.1) and rises → `enter(1)`. The player then keeps holding
660
- `←`, which is what charge play *is*. Under revision 2 the window ran from the advance, so any charge longer
661
- than `duration + step_timeout` expired before the flick and the move was silently lost; raising the timeout
662
- instead produced ghost charges minutes later. Under §5.2 step 1, `steps[0].test` — the hold — is still true
663
- while `←` is held, so `window` is refreshed on every tick. Release `←` at t=8.0 and the window opens *there*;
664
- `→` within `step_timeout` fires. Return to neutral instead and the window expires normally, so there is no
665
- ghost.
666
-
667
- **Motion plus button, `236 + P`** — append `InputTriggerGamepadButton.from(GamepadButtons.x)` as a fourth step.
668
- While `→` is held, step 1 keeps refreshing the window, so the button may be pressed at leisure; it fires with
669
- `→` still held because button steps are subset tests.
670
-
671
- ### 5.5 What this deliberately cannot do
672
-
673
- **Strict rejection.** `↓ ← →` inside the window fires the quarter-circle, because `←` neither advances nor
674
- resets. Arcade-grade matchers reject a direction outside the expected set. The shape of the fix is known — an
675
- optional rejection predicate on the sequence, resetting to 0 when it matches — and it is **deferred until a
676
- consumer asks**, on the position that the common case should be the only case. For every sequence in this
677
- codebase, and for menu, cheat-code and editor cases, leniency is the wanted behaviour.
678
-
679
- **Button buffering.** A button pressed *before* the motion step it follows poisons that step's entry snapshot
680
- and must be re-pressed. Genre-standard buffering is a fighting-game engine feature, not an input-mapping one.
681
-
682
- **Tap versus hold on the same switch — accepted as a cost of zero latency.** Given
683
- `'interact' = Key(e)` and `'loot_all' = Hold(Key(e), 0.5)`, a press-and-hold fires **both**: `'interact'` at
684
- `e↓`, `'loot_all'` 0.5 s later. They complete on different edges, so §4.6's per-edge consumption cannot
685
- arbitrate them, and no composition inside the algebra separates them either —
686
- `Chord(Release(e), Release(Hold(e, 0.5)))` collapses, because `hold.test` falls to false on the same `e↑` edge
687
- the release rises on.
688
-
689
- Discriminating them requires *withholding* the short action until the hold window has passed, which is
690
- precisely the frame-buffered dispatch this design exists to avoid. Paying that architectural cost — a second,
691
- deferred dispatch path with its own ordering and occlusion semantics — for one interaction pattern is not
692
- worth it. **Tap-and-hold pairs must use distinct inputs** (`E` and `Shift+E`), or accept that the hold is
693
- additive to the tap. Revision 2 claimed §4.6 arbitrated this; it does not.
694
-
695
- **Key autorepeat.** `KeyboardDevice` discards `event.repeat` (`KeyboardDevice.js:93-97`) and
696
- `InputDeviceSwitch.press()` is idempotent, so a held key produces exactly one edge. Autorepeat is not
697
- observable anywhere in this design, and a consumer that wants repeat behaviour — held-key menu navigation,
698
- `EditorKeyMap`'s current repeat-undo — must drive it from `isActive` plus its own timer. Changing this means
699
- changing `KeyboardDevice`, which is out of scope.
700
-
701
- ### 5.6 Why `SimpleStateMachine` is not used
702
-
703
- The first revision built this on
704
- [`SimpleStateMachine`](../../../../core/fsm/simple/SimpleStateMachine.js) and
705
- [`SimpleStateMachineDescription`](../../../../core/fsm/simple/SimpleStateMachineDescription.js). It does not
706
- earn its place here, and the reasons are specific rather than a matter of taste:
707
-
708
- * **The graph is a path.** States `0..N` with edges `i → i+1` plus resets to `0`. There is nothing to search,
709
- so `findPath` — the description's real capability — is dead weight.
710
- * **The self-transition is not declarable.** "Stay in the current state on a mismatch" is the common case, and
711
- the edge set never contains `i → i`. It works only because the edge-existence assertion in `advance()` is
712
- commented out (`SimpleStateMachine.js:238`) — a latent trap if those assertions are ever restored.
713
- * **Initial state is `-1`** (`SimpleStateMachine.js:16`), so every instance needs an explicit `setState(0)`.
714
- * **It is not the cheap object it looks like.** Each instance carries a description reference and two handler
715
- arrays (`SimpleStateMachine.js:22-30`) that this design never registers into — while `satisfied` and `window`
716
- have to live outside it regardless. Three typed-array slots replace all of it with no per-instance object.
717
-
718
- The requirement was to make good use of existing meep constructs. The honest reading is that this construct
719
- does not fit this problem: the matcher is a path, not a graph. **`SimpleStateMachineDescription` becomes the
720
- right tool the moment sequences branch** — alternate motions sharing a prefix, resolved by longest match —
721
- and that is the point at which to adopt it, not before.
722
-
723
- ---
724
-
725
- ## 6. Actions — the bindings/behaviour seam
726
-
727
- ### 6.1 The seam
728
-
729
- ```
730
- DATA (serializable, no code) | CODE (never serialized)
731
- ----------------------------- | ----------------------
732
- trigger ----> action name | action name ----> behaviour
733
- Chord(ctrl, z) "undo" | "undo" ----> editor.actions.undo()
734
- ```
735
-
736
- The left side is an `InputMap` component. The right side is an ECS event listener. They meet at a string, and
737
- nothing else crosses. This is the same seam [`InputSystem`](../systems/InputSystem.js) already uses
738
- (`InputSystem.js:79`), which is why that component is the one worth keeping the shape of. It is explicitly
739
- *not* a string-keyed factory registry: no behaviour is constructed from the string and no class is looked up
740
- by it.
741
-
742
- ### 6.2 One event, one level, one coordinate
743
-
744
- ```js
745
- // discrete — fires on the trigger's rising edge, this instant, no frame delay
746
- ecd.addEntityEventListener(entity, 'undo', () => editor.actions.undo());
747
-
748
- // continuous — no listener, no mirrored boolean, no teardown handler
749
- if (map.isActive('sprint')) { ... }
750
- map.axis2(v, 'move');
751
- ```
752
-
753
- Three accessors for three *questions* — "did it happen", "is it happening", "how much" — not three ways to ask
754
- one question. The level accessor is what deletes
755
- [`KeyboardCameraController`](../controllers/KeyboardCameraController.js)'s `controls` struct, the 16 `bind()`
756
- calls maintaining it, and the `clearControls` handler wired to `on.unlinked` that exists solely to stop the
757
- camera drifting after teardown. The same boilerplate is duplicated almost verbatim in
758
- [`prototype_first_person_controller.js`](../../../control/first-person/prototype_first_person_controller.js).
759
-
760
- Two things `isActive` is not: it is effectively always `false` for a sequence action, whose `test` is a
761
- single-edge pulse; and it returns `false` for every action on a **suppressed** map (§8).
762
-
763
- *(One behaviour `KeyboardCameraController` has that the coordinate does not reproduce: it sends a `'user-input'`
764
- entity event on every pan keydown (`KeyboardCameraController.js:54`), consumed elsewhere as an interaction
765
- signal. Porting it is a `map.bind('user-input', ...)` alongside the coordinate, and §14 lists it rather than
766
- leaving it to be discovered missing.)*
767
-
768
- ### 6.3 The event payload
769
-
770
- ```js
771
- export class InputActionEvent {
772
- action = "";
773
- /** Driving coordinate, or the pointer position, at the moment the trigger fired. @type {Vector3} */
774
- value = new Vector3();
775
- /** The switch whose edge completed the trigger. For a synthetic hold-maturity edge, the first
776
- * switch in the hold's own source set. @type {InputSource} */
777
- source = new InputSource();
778
- /** Arrival timestamp of the completing edge, real time, seconds since ISM start. @type {number} */
779
- time = 0;
780
- }
781
- ```
782
-
783
- One instance, reused across dispatches, mutated in place. Dispatch is synchronous, so a handler that reads it
784
- inline is correct and one that retains it is not — stated in the JSDoc, as scratch vectors are elsewhere in
785
- the engine. A nested edge cannot clobber it mid-read, because nested edges are queued rather than processed
786
- inline (§2.3).
787
-
788
- **It carries no DOM information**, and that is a real limit worth naming: consumers that today gate on
789
- `event.target === graphics.domElement` to tell a canvas click from a click on overlaying UI —
790
- `CombatInteractionStateMachine` does — have no equivalent here. UI-overlay occlusion is a DOM-tree question
791
- that §8's map occlusion cannot see. Those consumers are in the gesture set that stays on `InputController`
792
- (§1.1) for now; a DOM-aware gate is part of the deferred gesture work.
793
-
794
- ---
795
-
796
- ## 7. Layers — a stack, addressed by index
797
-
798
- Layers solve **rebinding**: defaults, then the player's overrides, then a context-specific tweak — one
799
- conceptual keymap assembled from stacked sources, top wins. They are a stack, addressed by index.
800
-
801
- ```js
802
- const map = new InputMap();
803
-
804
- map.bind('jump', InputTriggerKey.from('space')); // layer 0, the base
805
- map.bind('undo', InputTriggerChord.from(InputTriggerKey.from('ctrl'),
806
- InputTriggerKey.from('z')));
807
-
808
- const user = map.pushLayer(); // index 1
809
- user.bind('jump', InputTriggerGamepadButton.from(GamepadButtons.a)); // REPLACES space
810
- ```
811
-
812
- ```js
813
- map.layer(i) // InputLayer at index i, asserts existence
814
- map.pushLayer() // append, return the new top
815
- map.popLayer() // discard the top
816
- map.bind(...) // sugar for map.layer(0).bind(...)
817
- ```
818
-
819
- Rules, and they are CSS's:
820
-
821
- * Higher index is higher in the cascade.
822
- * The cascade is **per action name**, and it applies identically to trigger bindings and coordinate bindings
823
- (§9.1). The highest layer binding an action supplies *all* of that action's triggers or channels; lower
824
- layers' entries for it are inert.
825
- * To keep both, bind both **in one layer** — a binding maps one action to a *list*, exactly as
826
- `font-family: a, b` lists fallbacks in one declaration:
827
-
828
- ```js
829
- map.bind('jump', InputTriggerKey.from('space'), InputTriggerGamepadButton.from(GamepadButtons.a));
830
- ```
831
-
832
- Replace-versus-add is a property of *where you write it*, not a flag on `bind`. That is what lets rebinding
833
- ("the user's jump wins") and multi-device ("either device jumps") both be expressible with no mode switch.
834
-
835
- * An action named in the top layer that binds it, with nothing bound, is **unbound** — how a keymap layer
836
- removes an inherited binding:
837
-
838
- ```js
839
- map.layer(1).unbind('undo');
840
- ```
841
-
842
- * An action name is either a trigger action or a coordinate action, never both — asserted at authoring time,
843
- because `isActive('move')` and `axis2(v, 'move')` would otherwise disagree about what `'move'` is.
844
-
845
- Layers carry no names. There is nothing to look up, so nothing to mistype, and serialization is one `uint16`
846
- count with no strings (§10).
847
-
848
- ---
849
-
850
- ## 8. Occlusion — arbitration across entities
851
-
852
- Layers are within one map. Modality is across maps, and it is a different question: the cascade is *nominal*
853
- (over action names), occlusion is *physical* (over switches).
854
-
855
- ```js
856
- export const InputOcclusion = {
857
- /** Sees everything, blocks nothing. HUD overlays, debug readouts, recorders. */
858
- Pass: 0,
859
- /** Switches consumed by this map's fired triggers are invisible below, for that edge. The default. */
860
- BlockMatched: 1,
861
- /** Every map below is suppressed outright. Modal dialogs, pause menus. */
862
- BlockAll: 2
863
- };
864
- ```
865
-
866
- **The two values do different jobs at different scopes, and revision 2's failure to say so was a real bug.**
867
-
868
- ### 8.1 `BlockMatched` — dynamic, per edge, dispatch only
869
-
870
- Per edge, the walk descends maps by `order`. A `consumed : BitSet` over interned switch indices accumulates as
871
- it goes; a fired trigger adds its own switches, and a trigger whose switches are already consumed does not
872
- dispatch.
873
-
874
- **Consumption gates dispatch, not evaluation** — and this is the correction that matters, because the obvious
875
- alternative is broken. Hiding a consumed edge from a lower map entirely freezes that map's triggers at their
876
- last observed values, and the next edge they *do* see no longer looks like a change. Concretely: an upper map
877
- binding `Release(k)` and a lower map binding `k`. Every release is consumed above, so the lower map never
878
- observes `k` going up; its press trigger fires once and is then dead forever. So lower maps keep evaluating,
879
- and only their dispatch is suppressed.
880
-
881
- The honest consequence, since an earlier revision of this document claimed otherwise: a modal-consumed
882
- keystroke **does** advance a lower map's sequence and **does** run its hold timers. Only the firing is
883
- withheld. `BlockAll` is what makes a lower map genuinely inert (§8.2), and that is another reason the two
884
- values are not degrees of the same thing.
885
-
886
- Consumption is also **unconditional within a map**. Specificity arbitration — `Ctrl+Z` beating a bare `Z`
887
- (§4.6) — is a property of the map, not of its occlusion, so it holds in a `Pass` map too. What occlusion
888
- decides is only whether that consumption is still standing when the next map down is walked.
889
-
890
- `BlockMatched` does not affect polled reads. A pause menu using it stops the combat scene's `Escape` while the
891
- camera keeps panning behind it — which is exactly what §8 wants of it.
892
-
893
- ### 8.2 `BlockAll` — static, whole-map, dispatch *and* reads
894
-
895
- Each linked map carries a computed `suppressed : boolean` — "is there a `BlockAll` map above me". It is
896
- recomputed when the linked map set, any `order`, or any `occlusion` changes; not per edge.
897
-
898
- A suppressed map is **inert**: the walk skips it, so nothing dispatches; `isActive` returns `false`; coordinate
899
- reads return zero; sequence matchers do not advance and hold timers do not run. On un-suppression every
900
- sequence matcher is re-entered with `enter(0, held, now)` (§5.1), so it resumes against current reality rather
901
- than stale state.
902
-
903
- This is the fix for the defect revision 2 shipped. There, occlusion was a per-edge `consumed` set gating
904
- *firing* only, while `isActive` and the coordinate accessors are **polled** — outside any edge walk, with
905
- nothing connecting them. So the §11 pause menu stopped edge-driven actions while the §11 camera, which is a
906
- `Script` polling `map.axis2(pan, 'pan')` over WASD every tick, kept panning: precisely the drifting-world bug
907
- the modal value exists to prevent. Its two occlusion test cases both passed, because both tested dispatched
908
- actions only.
909
-
910
- ### 8.3 Consequences worth stating
911
-
912
- * **Local co-op needs no fourth value.** `BlockMatched` is per switch, so player 2's pad is untouched by player
913
- 1's map — they never reference the same switch. `BlockAll` suppresses whole maps and is therefore wrong for
914
- split-screen; that is a property of the modal hammer, not a hole in it.
915
- * **`order` ties are resolved by link order**, oldest first. Deterministic, and mentioned because the format
916
- clamps `order` (§10) and ties are therefore reachable.
917
- * **Lifetime is entity lifetime.** This is already how the game scopes input:
918
- `CombatInteractionStateMachine` registers an entity per FSM state and lets `link`/`unlink` turn bindings on
919
- and off (`CombatInteractionStateMachine.js:113-160`). Put the `InputMap` on the entity owning the context and
920
- entering or leaving the state *is* the binding lifecycle — no `enable()`/`disable()` bookkeeping, no
921
- `clearControls` on teardown.
922
-
923
- ---
924
-
925
- ## 9. The component and the system
926
-
927
- ### 9.1 `InputMap`
928
-
929
- ```js
930
- export class InputMap {
931
- static typeName = "InputMap";
932
- static serializable = true;
933
-
934
- /** Index 0 is the base. @readonly @type {InputLayer[]} */
935
- layers = new List();
936
-
937
- /** Cross-entity arbitration, higher evaluates first. @type {number} */
938
- order = 0;
939
-
940
- /** @type {InputOcclusion} */
941
- occlusion = InputOcclusion.BlockMatched;
942
-
943
- // ---- authoring -----------------------------------------------------
944
- bind(action, ...triggers) { } // -> InputMap, writes to layer 0
945
- bindCoordinate(action, ...channels) { } // -> InputMap, writes to layer 0
946
- unbind(action) { } // -> InputMap, covers both kinds
947
- layer(index) { } // -> InputLayer
948
- pushLayer() { } // -> InputLayer
949
- popLayer() { }
950
-
951
- // ---- reading -------------------------------------------------------
952
- isActive(action) { } // -> boolean
953
- coordinate(result, offset, action) { } // any dimensionality
954
- axis1(result, action) { } // Vector1
955
- axis2(result, action) { } // Vector2
956
- axis3(result, action) { } // Vector3
957
-
958
- equals(other) { }
959
- hash() { }
960
- }
961
- ```
962
-
963
- ```js
964
- export class InputLayer {
965
- /** @readonly @type {InputBinding[]} */
966
- bindings = new List();
967
- /** @readonly @type {List<InputCoordinateBinding>} */
968
- coordinates = new List();
969
-
970
- bind(action, ...triggers) { }
971
- bindCoordinate(action, ...channels) { }
972
- unbind(action) { }
973
- }
974
-
975
- export class InputBinding {
976
- action = "";
977
- /** @readonly @type {InputTrigger[]} */
978
- triggers = new List();
979
- }
980
-
981
- export class InputCoordinateBinding {
982
- action = "";
983
- /** @readonly @type {InputCoordinateChannel[]} */
984
- channels = new List();
985
- }
986
- ```
987
-
988
- **Coordinate bindings are a first-class list on the layer, not triggers.** Revision 2 exposed
989
- `bindCoordinate()` with nowhere to store the result — `InputBinding` holds triggers, and a channel is not a
990
- trigger — so coordinate bindings existed in the API, in the examples and in the argument for deleting
991
- `KeyboardCameraController`, but in neither the component nor the wire format.
992
-
993
- **Multiple channels on one action resolve by priority: the first channel with any non-zero component wins.**
994
- Deterministic, allocation-free, and the behaviour consumers already hand-write — `GamepadDevice`'s own
995
- documented example is "stick wins, else D-pad". It also avoids the alternative's arithmetic nonsense, where
996
- keyboard `D` plus a stick at 0.3 would sum to 1.3. All channels bound to one action must agree on
997
- `dimensions`; asserted at authoring time.
998
-
999
- The simple case never mentions layers, occlusion or order:
1000
-
1001
- ```js
1002
- const map = new InputMap();
1003
- map.bind('jump', InputTriggerKey.from('space'));
1004
- ```
1005
-
1006
- ### 9.2 `InputMapSystem`
1007
-
1008
- ```js
1009
- export class InputMapSystem extends System {
1010
- dependencies = [InputMap];
1011
-
1012
- registerDevice(adapter) { } // may be called after maps are linked; recompiles them (§3.1)
1013
-
1014
- link(map, entity) { } // compile (§9.3), subscribe to newly-referenced switches
1015
- unlink(map, entity) { } // release; unsubscribe switches no live map references
1016
- update(dt) { } // time only: hold maturity, sequence windows, Delta reset, suppression
1017
-
1018
- setRecorder(recorder) { } // §2.5 — DESIGNED, NOT BUILT
1019
- }
1020
- ```
1021
-
1022
- Construction takes adapters rather than `engine.devices`, so the system has no dependency on the shape of that
1023
- object and is constructible in a test with nothing behind it:
1024
-
1025
- ```js
1026
- new InputMapSystem([
1027
- new KeyboardInputDeviceAdapter(devices.keyboard),
1028
- new PointerInputDeviceAdapter(devices.pointer),
1029
- new GamepadInputDeviceAdapter(devices.gamepad)
1030
- ]);
1031
- ```
1032
-
1033
- Each interned switch is subscribed once, with a per-switch `SourceBinding {index, system}` passed as the
1034
- `Signal` handler's context (`Signal.js:151`), so the edge path allocates nothing and needs no closure per
1035
- event.
1036
-
1037
- *(As built, the system declares no `components_used`, and does not need to: relative channels LATCH rather
1038
- than clear (§3.3), so every reader in a frame sees the same value regardless of where the system runs in it.
1039
- An earlier revision of this section leaned on execution order instead, which was the weaker guarantee.)*
1040
-
1041
- **Unlinking a map with a sequence mid-match** discards its matcher state along with the compiled program;
1042
- there is nothing to leak and nothing to resume. Unlinking with switches held is likewise clean: `#held` is
1043
- owned by the system, not the map, and the map's triggers are simply gone.
1044
-
1045
- ### 9.3 Compilation: the stylesheet / computed-style split
1046
-
1047
- The `InputMap` component is *authored* data — layered, editable. It is not what the edge path reads. On `link`,
1048
- on `registerDevice`, and on any structural change, the system compiles it into an internal `InputMapProgram`:
1049
-
1050
- * cascade resolved — one flat action table per kind, layers already collapsed;
1051
- * switches interned to `Uint32` indices against a system-wide table;
1052
- * per switch, the list of triggers it can affect (`collectSources`), pre-sorted into §4.6 specificity order, so
1053
- an edge touches only triggers that could possibly care;
1054
- * every trigger's `satisfied` initialised from current state, not from `false` (§4.5);
1055
- * sequence matcher state allocated as three typed-array slots per sequence binding (§5.1).
1056
-
1057
- The edge path then touches integers, typed arrays and `BitSet`s only. No string comparison, no `List`
1058
- traversal, no allocation per event. Recompiles requested during dispatch are queued and applied at the end of
1059
- the current edge (§2.3), so a walk always completes against the program it started with.
1060
-
1061
- ---
1062
-
1063
- ## 10. Serialization
1064
-
1065
- ```js
1066
- export class InputMapSerializationAdapter extends BinaryClassSerializationAdapter {
1067
- klass = InputMap;
1068
- version = 0;
1069
-
1070
- serialize(buffer, map) { }
1071
- deserialize(buffer, map) { }
1072
- }
1073
- ```
1074
-
1075
- ```
1076
- InputMap
1077
- int16 order see below
1078
- uint8 occlusion InputOcclusion
1079
- uint8 device_count
1080
- repeat device_count x utf8 device table - every InputDeviceAdapter#id referenced
1081
- uint16 layer_count
1082
- repeat layer_count x InputLayer index 0 first; layers carry no name
1083
-
1084
- InputLayer
1085
- uint16 binding_count
1086
- repeat binding_count x InputBinding
1087
- uint16 coordinate_binding_count
1088
- repeat coordinate_binding_count x InputCoordinateBinding
1089
-
1090
- InputBinding
1091
- utf8 action
1092
- uint8 trigger_count
1093
- repeat trigger_count x InputTrigger
1094
-
1095
- InputCoordinateBinding
1096
- utf8 action
1097
- uint8 channel_count
1098
- repeat channel_count x InputCoordinateChannel
1099
-
1100
- InputTrigger
1101
- uint8 type InputTriggerType
1102
- Switch : InputSource
1103
- Direction : InputCoordinateChannel, uint8 direction, float32 threshold, float32 hysteresis
1104
- Chord : uint8 child_count, children
1105
- Sequence : uint8 child_count, children, float32 step_timeout
1106
- Hold : child, float32 duration
1107
- Release : child
1108
-
1109
- InputCoordinateChannel
1110
- uint8 type InputCoordinateChannelType
1111
- Device : InputCoordinateSource
1112
- Switches : uint8 count, count x InputTrigger (2 per dimension: negative, positive)
1113
- Compose : uint8 count, count x InputCoordinateChannel
1114
-
1115
- InputSource
1116
- uint8 device_index into the map's device table
1117
- uint16 code
1118
-
1119
- InputCoordinateSource
1120
- uint8 device_index
1121
- uint16 code
1122
- uint8 dimensions
1123
- uint8 kind InputCoordinateKind
1124
- ```
1125
-
1126
- Decisions, not incidentals:
1127
-
1128
- * **`order` is `int16`.** Revision 2 wrote it as `uint8 + 128`, range `[-128, 127]` — and its own modal example
1129
- used `order = 1000`, which clamped to 127 on load and could silently tie with any other high map. `int16`
1130
- covers the examples and leaves headroom; ties resolve by link order (§8.3).
1131
- * **Coordinate bindings are reachable from the root.** Revision 2 defined an `InputCoordinateChannel` encoding
1132
- that nothing referenced, so no map containing a coordinate could round-trip.
1133
- * **`Direction` serializes a channel, not a source.** It accepts any `InputCoordinate*` channel (§4.2), so
1134
- writing only an `InputCoordinateSource` made direction-over-composed-channel constructible and
1135
- unserializable.
1136
- * **A device table, not a string per source.** A 40-binding keyboard map writes `"keyboard"` once rather than
1137
- forty times.
1138
- * **Every string here is an identifier.** `writeUTF8String` is ASCII-only in practice and `readUTF8String` is
1139
- NUL-terminated by design, so this must be stated: action names and device ids are ASCII identifiers, not
1140
- localized text. A binding UI shows a label looked up *from* the action name; it never serializes the label.
1141
- * **Codes come from enums, three of which are new.** `KeyCodes`, `GamepadButtons` and `GamepadAxes` are already
1142
- stable on-disk quantities; `MouseButtons`, `PointerCoordinates` and `GamepadCoordinates` are introduced here
1143
- and are append-only once shipped (§3.1). Revision 2 claimed "no new numbering to keep in sync", which was
1144
- false the moment it introduced them.
1145
- * **Triggers and channels nest, so serialization recurses.** One `uint8` discriminator each, dispatched by a
1146
- `switch` — verbose, typed and greppable, over a string-keyed constructor table.
1147
-
1148
- ---
1149
-
1150
- ## 11. End to end
1151
-
1152
- **Gameplay — camera, mixed devices:**
1153
-
1154
- ```js
1155
- const map = new InputMap();
1156
-
1157
- map.bindCoordinate('pan',
1158
- InputCoordinateGamepad.from(GamepadCoordinates.left_stick), // first non-zero wins
1159
- InputCoordinateSwitches.from([
1160
- InputTriggerKey.from('a'), InputTriggerKey.from('d'),
1161
- InputTriggerKey.from('w'), InputTriggerKey.from('s')
1162
- ]));
1163
- map.bind('user-input', InputTriggerKey.from('a'), InputTriggerKey.from('d'),
1164
- InputTriggerKey.from('w'), InputTriggerKey.from('s'));
1165
- map.bind('focus_selection', InputTriggerKey.from('f'));
1166
-
1167
- new Entity()
1168
- .add(map)
1169
- .add(new Script(dt => {
1170
- map.axis2(pan, 'pan');
1171
- apply_radial_dead_zone(pan, pan.x, pan.y, 0.15, 0.95); // policy, at the call site
1172
- controller.pan(pan, dt);
1173
- }))
1174
- .build(ecd);
1175
- ```
1176
-
1177
- Both channels feed one action in one call, so the cascade replaces them as a unit; the stick wins while
1178
- deflected and the keys take over when it rests. The `'user-input'` binding is
1179
- `KeyboardCameraController.js:54`'s interaction signal, carried over explicitly rather than lost.
1180
-
1181
- **Editor — `EditorKeyMap` as data:**
1182
-
1183
- ```js
1184
- const ctrl = InputTriggerKey.from('ctrl');
1185
-
1186
- const map = new InputMap();
1187
- map.order = 100;
1188
-
1189
- map.bind('undo', InputTriggerChord.from(ctrl, InputTriggerKey.from('z')));
1190
- map.bind('redo', InputTriggerChord.from(ctrl, InputTriggerKey.from('y')));
1191
- map.bind('copy', InputTriggerChord.from(ctrl, InputTriggerKey.from('c')));
1192
- map.bind('paste', InputTriggerChord.from(ctrl, InputTriggerKey.from('v')));
1193
- map.bind('save', InputTriggerChord.from(ctrl, InputTriggerKey.from('s')));
1194
- map.bind('duplicate', InputTriggerChord.from(InputTriggerKey.from('shift'), InputTriggerKey.from('d')));
1195
- map.bind('delete', InputTriggerKey.from('delete'), InputTriggerKey.from('x'));
1196
-
1197
- ecd.addEntityEventListener(entity, 'undo', () => editor.actions.undo());
1198
- ```
1199
-
1200
- `Ctrl+Z` beats the bare `Z` binding under §4.6 (modifier-first; see §4.6's stated condition), and the
1201
- `if (event.ctrlKey)` split disappears. The bindings are serializable, so the editor gets a rebinding panel for
1202
- free — the thing that could not exist while they were `if` statements. §14.1 lists the three `EditorKeyMap`
1203
- behaviours that do *not* come across.
1204
-
1205
- **Modal — pause menu:**
1206
-
1207
- ```js
1208
- const map = new InputMap();
1209
- map.order = 1000;
1210
- map.occlusion = InputOcclusion.BlockAll;
1211
- map.bind('resume', InputTriggerKey.from('escape'));
1212
- ```
1213
-
1214
- Every map below is suppressed: no dispatch, and `isActive`/`axis2` read neutral — so the camera above stops
1215
- rather than drifting behind the dialog (§8.2).
1216
-
1217
- **Cross-device chords:**
1218
-
1219
- ```js
1220
- map.bind('precision_place', InputTriggerChord.from(InputTriggerKey.from('ctrl'),
1221
- InputTriggerMouseButton.from(MouseButtons.left)));
1222
- map.bind('pad_reset', InputTriggerChord.from(InputTriggerGamepadButton.from(GamepadButtons.back),
1223
- InputTriggerKey.from('r')));
1224
- ```
1225
-
1226
- ---
1227
-
1228
- ## 12. Testing
1229
-
1230
- ### 12.1 No mocks, and no DOM
1231
-
1232
- The seam is `InputDeviceSwitch`: a real object with `press()`/`release()`, constructible in node with nothing
1233
- beneath it. A test adapter is a *real* adapter over *real* switches — not a mock. Eager evaluation keeps the
1234
- tests short, because there is no tick to pump for edge-driven behaviour:
1235
-
1236
- ```js
1237
- const kb = new TestInputDeviceAdapter('keyboard', KeyCodes);
1238
- const system = new InputMapSystem([kb]);
1239
- // real EntityComponentDataset, real InputMap, real listeners
1240
-
1241
- kb.switch(KeyCodes.ctrl).press();
1242
- kb.switch(KeyCodes.z).press();
1243
-
1244
- expect(observed).toEqual(['undo']); // no system.update() anywhere
1245
- ```
1246
-
1247
- `system.update(dt)` appears only in tests for hold maturity, sequence windows, `Delta` reset and suppression —
1248
- a precise statement of what the tick is for.
1249
-
1250
- *(Revision 2 justified this by claiming `InputSystem` "cannot be exercised without a focusable DOM element".
1251
- That was overstated: focusability is `KeyboardDevice`'s requirement (`KeyboardDevice.js:61-66`), and
1252
- `InputSystem` resolves paths against any object, so a synthetic tree of real Signals would exercise it. The
1253
- real point is narrower and still worth making — nobody has, and `InputBinding.spec.js` asserts only that a
1254
- constructor does not throw.)*
1255
-
1256
- ### 12.2 What gets asserted
1257
-
1258
- Observable behaviour only: dispatched action names and order, `isActive` levels, coordinate values. Never which
1259
- triggers were evaluated, never how many times a matcher ran, never internal matcher state.
1260
-
1261
- | | Case | Guards |
1262
- |---|---|---|
1263
- | 1 | An action fires before any `update()` runs | §2 eager evaluation — the requirement, as a test |
1264
- | 2 | Press and release inside one frame both fire; two presses dispatch in press order | §2 |
1265
- | 3 | `Ctrl` then `Z` fires only `undo`; `Z` then `Ctrl` fires both, in that order | §4.6 and its stated condition |
1266
- | 4 | One long press of `A` does not satisfy both steps of `[A, A]` | §5.1 entry snapshot |
1267
- | 5 | `[A,A,B]` fires on `A A A B` | §5.3 |
1268
- | 6 | Holding `A` and tapping `B` twice fires `[A,B]` **once** | §5.3 post-fire re-entry — the revision-2 machine-gun |
1269
- | 7 | A window expiry with the first step's key held does not spuriously advance | §5.1 — the revision-2 stale-snapshot bug |
1270
- | 8 | `Ctrl+Shift+A > B` fires whether or not modifiers are released first | §5.4 |
1271
- | 9 | `↓ ↘ →` over a stick coordinate fires; the same over raw d-pad *buttons* does not | §5.4 — the exclusivity claim, both directions |
1272
- | 10 | Charge (`hold ← 2 s > →`) fires after a 10 s hold; returning to neutral instead expires it | §5.2 window refresh — the revision-2 charge failure |
1273
- | 11 | A sequence window expires only after the previous step stops holding | §5.2 |
1274
- | 12 | `Release(Chord(a,b))` does not fire on the first edge touching `a` | §4.5 `satisfied` initialisation |
1275
- | 13 | A handler that removes its own entity, or rebinds its own map, does not corrupt the walk | §2.3 |
1276
- | 14 | A `BlockAll` map zeroes a lower map's `axis2` and `isActive`, not just its dispatch | §8.2 — the revision-2 drifting camera |
1277
- | 15 | A `BlockMatched` map does **not** stop a lower map's unrelated action or its coordinate reads | §8.1 — the converse |
1278
- | 16 | A lower map's press binding still fires after many edges consumed above it | §8.1 — the stale-edge trap |
1279
- | 17 | Un-suppressing a map re-enters its sequences at 0 rather than resuming stale state | §8.2 |
1280
- | 18 | The top layer's `jump` replaces the base layer's `jump`; two triggers in one layer both fire | §7 |
1281
- | 19 | A coordinate action bound to stick + keys reads the stick while deflected, the keys at rest | §9.1 priority |
1282
- | 20 | A gamepad binding with no pad connected links and never fires; so does an unregistered device id | §3.1 totality |
1283
- | 21 | `registerDevice` after link makes previously inert bindings live | §3.1, §9.3 |
1284
- | 22 | Two wheel events in one frame sum for tick-time readers | §3.3 `Delta` |
1285
- | 23 | A 3-D wheel coordinate round-trips all three components | §3.3 dimensionality |
1286
- | 24 | Serialize, deserialize, replay a recorded trace, get the same dispatches | §10, §2.5 |
1287
-
1288
- Cases 6, 7, 10 and 14 are regressions against defects revision 2 actually contained; write them first. Case 9
1289
- is the only claim in §5.4 that could be wrong in a way the others would not catch.
1290
-
1291
- ---
1292
-
1293
- ## 13. The trigger DSL — planned, deferred, optional
1294
-
1295
- **Not implemented in this design.** Specified here so the trigger classes are shaped to accept it, and so it
1296
- is not invented ad hoc later.
1297
-
1298
- ### 13.1 What it is
1299
-
1300
- A text syntax compiling to the same `InputTrigger` objects §4 defines. It is **an additional entry point,
1301
- never a required one**: an author can compose triggers by hand or write them as text, and the runtime cannot
1302
- tell the results apart.
1303
-
1304
- ```js
1305
- import { InputTriggerDSL } from "./dsl/InputTriggerDSL.js";
1306
-
1307
- map.bind('promote', InputTriggerDSL.compile("keyboard:ctrl + keyboard:shift + keyboard:a > keyboard:b"));
1308
- ```
1309
-
1310
- ### 13.2 Grammar
1311
-
1312
- ```ebnf
1313
- trigger = sequence ;
1314
- sequence = chord , { ">" , chord } , [ "within" , NUMBER ] ;
1315
- chord = unary , { "+" , unary } ;
1316
- unary = [ "!" ] , primary ;
1317
- primary = atom | "(" , trigger , ")" | call ;
1318
- call = IDENT , "(" , [ trigger , { "," , trigger | NUMBER } ] , ")" ;
1319
- atom = DEVICE , ":" , BINDPOINT , [ "." , SUBPOINT ] ;
1320
- DEVICE = IDENT ;
1321
- BINDPOINT = IDENT | NUMBER ;
1322
- SUBPOINT = IDENT ;
1323
- ```
1324
-
1325
- | Form | Means | Compiles to |
1326
- |---|---|---|
1327
- | `keyboard:space` | a key | `InputTriggerKey` |
1328
- | `pointer:left` | a mouse button | `InputTriggerMouseButton` |
1329
- | `gamepad:a` | a pad button | `InputTriggerGamepadButton` |
1330
- | `gamepad:left_stick.down` | a direction on a coordinate | `InputTriggerDirection` |
1331
- | `a + b` | chord | `InputTriggerChord` |
1332
- | `a > b` | sequence | `InputTriggerSequence` |
1333
- | `!a` | release / negation | `InputTriggerRelease` |
1334
- | `hold(a, 0.5)` | held for 0.5 s | `InputTriggerHold` |
1335
- | `(...)` | grouping | — |
1336
- | `... within 0.2` | step timeout | `withStepTimeout` |
1337
-
1338
- **Precedence, tightest first:** `!` → `+` → `>` → `within`, so
1339
- `keyboard:ctrl + keyboard:shift + keyboard:a > keyboard:b` parses as intended with no parentheses.
1340
-
1341
- **Keyword collision** is resolved by the `:` — an identifier followed by `:` is a device name, so a device may
1342
- be called `hold` without ambiguity. Worth stating because it is where small DSLs usually break.
1343
-
1344
- The §5.4 motion, written out:
1345
-
1346
- ```
1347
- gamepad:left_stick.down > gamepad:left_stick.down_right > gamepad:left_stick.right + gamepad:x within 0.2
1348
- ```
1349
-
1350
- ### 13.3 Why it stays separate
1351
-
1352
- * **`ism/dsl/` is its own module**, imported by nothing in the runtime. A game that composes triggers by hand
1353
- never pays for a parser — it is not in the bundle.
1354
- * **It is a compiler, not a representation.** Serialization stays binary (§10), so a DSL change can never
1355
- invalidate saved bindings.
1356
- * **`InputTriggerDSL.stringify(trigger)` is the inverse**, which is what a rebinding UI wants: it shows a
1357
- binding in the syntax an author would type, with no second formatting path.
1358
-
1359
- ### 13.4 Deferred until
1360
-
1361
- Everything in §14 phases 1–8 has landed and at least one consumer has migrated. The DSL is a convenience over a
1362
- working system; building it first would freeze the trigger vocabulary before the vocabulary has been used.
1363
-
1364
- ---
1365
-
1366
- ## 14. Migration
1367
-
1368
- The ISM lands beside the existing systems. Both keep working; neither is touched until its consumers move.
1369
- **`InputController` and `InputControllerSystem` survive this program** — they still own every gesture binding
1370
- (§1.1).
1371
-
1372
- *This table is the plan of work, not a progress report. For where each phase actually stands, read the status
1373
- section of [`ISM_IMPLEMENTATION_PLAN.md`](ISM_IMPLEMENTATION_PLAN.md), which is kept current as phases land.*
1374
-
1375
- | Phase | Work | Gate |
1376
- |---|---|---|
1377
- | **1** | `InputSource`, `InputCoordinateSource`, the `InputTrigger*` classes, `InputMap` / `InputLayer` / `InputBinding` / `InputCoordinateBinding` | Value-object specs: `equals` / `hash` / round-trip |
1378
- | **2** | `InputDeviceAdapter` and the three adapters; the `MouseButtons`, `PointerCoordinates`, `GamepadCoordinates` enums | Totality: every declared code resolves with no hardware, and with no adapter (§12.2 cases 20–21) |
1379
- | **3** | `InputMapSystem`: eager edge path, `#held`, compile, switch/chord/release, cascade, `BlockMatched`, ECS dispatch, `isActive` | cases 1–3, 12, 13, 15, 18 |
1380
- | **4** | `InputTriggerSequence` + `InputTriggerHold`; synthetic maturity edges; window refresh and expiry | cases 4–8, 10, 11, 16 |
1381
- | **5** | Coordinates: channels, priority resolution, the `Delta` latch, read accessors | cases 19, 22, 23; retires `KeyboardCameraController` |
1382
- | **6** | `InputTriggerDirection` over coordinates, with threshold and hysteresis | case 9 |
1383
- | **7** | `BlockAll` suppression across dispatch and reads; un-suppression re-entry | cases 14, 17 |
1384
- | **8** | `InputMapSerializationAdapter` + `InputRecorder` | case 24 |
1385
- | **9** | Migrate consumers (below) | existing specs stay green |
1386
- | **10** | `ism/dsl/` — the §13 compiler and `stringify` | round-trip: `compile(stringify(t))` equals `t` |
1387
-
1388
- ### 14.1 Consumers
1389
-
1390
- 1. **`EditorKeyMap`** — **blocked, not retired.** The engine's `KeyboardDevice` is mounted on
1391
- `viewStack.el` (`Engine.js:367`) and `Editor.js:563` pushes the entire editor GUI into that same stack, so
1392
- an editor map would see keys typed into panel text fields — `x` would delete the selected entities, and
1393
- `preventDefault` would swallow the keystroke. The ISM has no DOM awareness to express the old gate (§6.3),
1394
- so retiring this needs a design addition. See the implementation plan §5.1. Four behaviours would have to be
1395
- re-provided in any case, because none of them is in the ISM:
1396
- * **Focus gating.** `isViewFocused` (`EditorKeyMap.js:12-31`) suppresses all editor bindings when focus is
1397
- outside the game view. In the ISM this depends on which element the editor's `KeyboardDevice` is mounted
1398
- on. With bare-key bindings like `x` = delete, getting it wrong is destructive — verify before landing.
1399
- * **Autorepeat.** Holding `Ctrl+Z` currently repeat-undoes; it will not (§5.5). Decide per action whether
1400
- that is a loss or a fix.
1401
- * **`stopPropagation()`** on handled chords — currently prevents the event reaching other listeners.
1402
- * **The unconditional forward to the active tool** (`EditorKeyMap.js:175-180`), which `Tool.handleKeyboardEvent`
1403
- relies on: each tool gets its own `InputMap` on its activation entity, at a higher `order`.
1404
- 2. **`KeyboardCameraController`** — retired by §3.3, not ported. Carry the `'user-input'` event (§11).
1405
- 3. **`Input` / `InputSystem`** — **done.** All five consumers moved and both are `@deprecated`.
1406
- `OverlayPageGUI` (escape), `CombatInteractionController` (p, escape) and two harnesses were straight
1407
- translations. `StoryManager` was a split: it bound both a tap and the space bar to one action, and only
1408
- the space bar moved. A tap is not a button press (§1.1) — translating it would change advance-on-release
1409
- to advance-on-press and fire at the start of every drag — so the tap kept a small `InputController`, which
1410
- is the decision the earlier revision of this section left open.
1411
- 4. **`InputController` keyboard bindings** — **not started.** The keyboard subset moves; `priority` becomes
1412
- `order`, `exclusive` becomes `InputOcclusion.BlockMatched`. Gesture bindings stay put, which for
1413
- `StrategyInteractionController` means most of the file. This is where escape arbitration finally bites
1414
- (plan §5.4) and where `KeyboardCameraController`'s `'user-input'` entity subtlety has to be handled.
1415
- 5. **`components/InputBinding.js`** (`{path, listener, …}`) is orphaned only when `InputController` finally
1416
- goes, which is not in this program.
1417
-
1418
- ### 14.2 A naming collision to plan around
1419
-
1420
- *(Resolved: the new record lives at `ism/map/InputBinding.js`, so the two never shared a filename and the old
1421
- one kept its path and its importer. What follows is the problem as it stood.)*
1422
-
1423
- Phase 1 introduces `InputBinding` in `ism/`, and `ism/InputBinding.js` already exists as the old
1424
- `{path, event}` record imported by [`Input.js:3`](../components/Input.js). The old file must survive until
1425
- `Input` is retired (phase 9), so the two cannot share a filename. Land the new one as `ism/InputBinding.js`
1426
- only after the old is deleted; until then it lives beside its siblings under a transitional name, or `Input`
1427
- is repointed at a copy under `components/`.
1428
-
1429
- ---
1430
-
1431
- ## 15. Explicitly not doing
1432
-
1433
- * **Pointer gestures** — tap, drag, move, wheel-as-gesture, pinch. §1.1, with the binding counts. Revision 2
1434
- claimed "nothing in the codebase binds pinch today"; that was false —
1435
- `StrategyInteractionController.js:437,447` binds `pinchStart` and `pinch`, and it is also the largest
1436
- migration target.
1437
- * **Buffering input to a frame boundary.** §2. Revision 1 did, and it bought nothing eager evaluation does not
1438
- also provide.
1439
- * **Discriminating tap from hold on one switch.** §5.5 — it requires withholding the short action, which is the
1440
- buffering above. Accepted as a cost.
1441
- * **Key autorepeat.** §5.5 — not observable through `InputDeviceSwitch` at all.
1442
- * **Keeping string paths.** `resolvePath` throws where it should resolve and cannot express two sources at once;
1443
- `GAMEPAD_API_PROPOSAL.md §5` says not to grow it.
1444
- * **A trigger class per gesture pattern.** Double-tap is a two-step sequence with a short window; long-press is
1445
- `InputTriggerHold`.
1446
- * **Dead zones, sensitivity or response curves on analog output.** §3.3. The direction threshold and hysteresis
1447
- in §3.4 are not exceptions: producing a boolean from a coordinate cannot be done without them.
1448
- * **A `Started` / `Performed` / `Canceled` phase enum, or an `edge` field.** §4.1 — every trigger is a
1449
- predicate and every binding fires on its rising edge.
1450
- * **A per-binding `consume` flag.** Occlusion is a property of a *context*, not of one binding, and a boolean
1451
- per binding makes behaviour depend on declaration order.
1452
- * **Named layers.** §7 — a stack is addressed by index.
1453
- * **Behaviour in the component.** No function references, no script names, no serialized handler ids (§6.1).
1454
- * **DOM-target awareness in `InputActionEvent`.** §6.3 — it belongs with the deferred gesture work.
1455
- * **Strict sequence rejection**, and **button buffering**. §5.5, both deferred with known shapes.
1456
- * **A generic input node graph.** The trigger classes are already a small algebra; the DSL (§13) is the cheaper
1457
- first step over it.
1458
-
1459
- ---
1460
-
1461
- ## 16. Resolved decisions
1462
-
1463
- 1. **Analog channels are N-dimensional "coordinates", not axes** — §3.3. `WheelEvent` has three components and
1464
- `PointerDevice` surfaces all three. `kind` (`Absolute` / `Delta` / `Normalized`) carries the verb.
1465
- 2. **`order` stays author-set** — §8. A GUI-hierarchy-derived order would need no hand-picked numbers but would
1466
- couple input arbitration to that hierarchy.
1467
- 3. **No sensitivity in bindings** — §3.3. A rebinding UI serializes *which* channel drives an action, never how
1468
- twitchy it is.
1469
- 4. **Layers are an index-addressed stack, with no names** — §7.
1470
- 5. **Evaluation is eager, on the edge** — §2. No buffer, no added frame. The costs — re-entrant dispatch,
1471
- arrival-ordering, the §4.6 chord condition, the §5.5 tap-versus-hold limit, and the §3.3 `Delta` split
1472
- between eager and tick-time readers — are stated where they arise rather than hidden.
1473
- 6. **The trigger DSL is planned, specified and deferred** — §13, phase 10.
1474
- 7. **Pointer gestures are out of scope** — §1.1. `InputController` is reduced, not retired.
1475
- 8. **`SimpleStateMachine` is not used** — §5.6. The matcher is a path, not a graph; the class becomes the right
1476
- tool if sequences ever branch.
1477
-
1478
- ---
1479
-
1480
- ## 17. What changed in revision 3, and why
1481
-
1482
- An adversarial review of revision 2 found three defects that made it unimplementable, plus a set of false
1483
- claims about the existing codebase. Everything below was verified against source before being changed.
1484
-
1485
- ### Defects
1486
-
1487
- | | Was | Now |
1488
- |---|---|---|
1489
- | **Sequence matcher** | Reset paths (window expiry, post-fire) did not re-establish the entry snapshot, and the advance step indexed `steps[N]`. Holding `A` and mashing `B` machine-gunned `[A,B]` off one `A` press; an expiry with the first key held spuriously advanced with no edge. | Every entry routes through `enter(k, held, now)` (§5.1), which snapshots and never indexes `steps[N]`. Cases 6 and 7. |
1490
- | **Occlusion** | A per-edge `consumed` set gating *firing* only. `isActive` and coordinate reads are polled, so `BlockAll` could not stop them — a pause menu left the camera panning, the exact bug §8 claimed to prevent. Both occlusion tests passed anyway, testing only dispatch. | Two scopes: `BlockMatched` stays dynamic and dispatch-only; `BlockAll` sets a static per-map `suppressed` covering dispatch *and* reads (§8.2). Cases 14, 15, 17. |
1491
- | **Coordinate bindings** | `bindCoordinate()` existed with nowhere to store its result, and §10's channel encoding was unreachable from the format root. No map with a coordinate could serialize. `bindCoordinate` was also called twice for one action in §11 with no defined meaning. | `InputCoordinateBinding` on the layer, reachable in the format, with priority resolution (§9.1). Case 19. |
1492
-
1493
- ### Corrections
1494
-
1495
- * **Charge inputs failed.** The step window ran from the advance, so any charge longer than
1496
- `duration + step_timeout` expired before the flick. It now measures time since the previous step *stopped*
1497
- holding (§5.2). Case 10.
1498
- * **`satisfied` initialisation was unspecified**, and zero-init makes `Release(Chord(a,b))` fire before the
1499
- chord exists. Now snapshotted at compile (§4.5). Case 12.
1500
- * **`order` was `uint8`-biased**, range `[-128,127]`, while §11's own modal used `1000`. Now `int16` (§10).
1501
- * **`MouseButtons` cited the wrong standard.** The `MouseEvent.buttons` *bitmask* order is left/right/middle;
1502
- `PointerDevice.buttons[]` is indexed by `MouseEvent.button` *ordinal*. Following the citation would have
1503
- fired every right-click binding on the middle button, into saved data (§3.1).
1504
- * **`Release(sequence)` was claimed to mean "stopped matching".** It cannot — a sequence's `test` is a pulse
1505
- (§4.3).
1506
- * **Specificity did not rank `Hold` / `Release` / `Direction`**, so they fell through to declaration order —
1507
- the accident §4.6 claims to eliminate (§4.6).
1508
- * **Chord-vs-subset arbitration was stated unconditionally.** It holds only when the modifier arrives first
1509
- (§4.6). Case 3 now tests both orders.
1510
- * **The `Delta` "same value for every reader" invariant** was a leftover from the buffered design and is false
1511
- under eager dispatch (§3.3).
1512
- * **Re-entrancy covered only structural change.** Nested edges and mid-dispatch binding edits are now specified
1513
- (§2.3).
1514
- * **The recorder captured only switch edges**, so no map with a coordinate or a hold could replay. It now
1515
- records coordinate samples and per-frame `dt` (§2.5).
1516
- * **Unregistered devices had no defined behaviour** (§3.1).
1517
- * **`EditorKeyMap` and `KeyboardCameraController` deletions dropped real behaviour** — focus gating,
1518
- autorepeat, `stopPropagation`, the tool forward, the `'user-input'` event — now inventoried (§14.1).
1519
- * **Counts and claims:** `KeyboardCameraController` has 16 `bind()` calls, not eight; the `InputController`
1520
- path-string count flattered a migration that is mostly gestures (§1.1); `InputControllerSystem`'s `deferred`
1521
- array is dead code, not a working mitigation (§2.3); "`InputSystem` cannot be tested without a DOM element"
1522
- was overstated (§12.1); "no new numbering to keep in sync" was false (§10); `PointerDevice`'s wheel reports
1523
- `sign()`, so a `Delta` wheel channel counts ticks rather than distance (§3.3).
1
+ # Input System Mapping — design
2
+
3
+ A binding layer for keyboard, pointer buttons and gamepad, replacing `Input`/`InputSystem`, the keyboard half
4
+ of `InputController`/`InputControllerSystem`, `EditorKeyMap` and `Tool.handleKeyboardEvent`.
5
+
6
+ Status: **design, not yet implemented.** The only file in this directory today is
7
+ [`InputBinding.js`](InputBinding.js) (the `{path, event}` record used by
8
+ [`Input`](../components/Input.js)); it is superseded by §4.
9
+
10
+ > **Revision 3.** Rewritten after an adversarial review of revision 2 found three defects that made it
11
+ > unimplementable, and several false claims about the existing codebase. What changed, and why, is in §17.
12
+ > The three that matter: the sequence matcher's reset paths were broken (§5); occlusion could not reach polled
13
+ > reads, so a modal dialog could not stop a camera (§8); and coordinate bindings had no home in either the
14
+ > component or the wire format (§9.1, §10). Two scope decisions were taken at the same time: **pointer
15
+ > gestures are out of scope** (§1.1), and **`SimpleStateMachine` is not used** (§5.6).
16
+
17
+ ---
18
+
19
+ ## 0. Summary
20
+
21
+ Two decisions carry the design.
22
+
23
+ > **1. Every input this system binds is either a switch or a coordinate.** `InputDeviceSwitch` already exists
24
+ > and is already what keyboard keys, pointer buttons and gamepad buttons *are*. A coordinate is an
25
+ > N-dimensional analog channel — stick deflection, wheel ticks, pointer position.
26
+ >
27
+ > **2. Every trigger is a predicate over (held switches + its own state), and a binding fires on that
28
+ > predicate's rising edge** — evaluated the instant an edge arrives, never on a frame boundary.
29
+
30
+ | Requirement | Falls out of |
31
+ |---|---|
32
+ | Combinations (chords) | A chord is the AND of its children's predicates |
33
+ | Sequences | A sequence is a predicate over a small linear matcher, advanced per edge (§5) |
34
+ | Cross-device combos | A chord cannot tell which device a child came from — there is nothing to make work |
35
+ | Arbitrary devices | A device is anything that hands out switches and coordinates (§3) |
36
+ | Layers, top wins | Cascade over an action table, compiled once per change, like CSS (§7) |
37
+ | Serializable bindings | A binding is `(trigger, action name)`; both halves are plain data (§6.1, §10) |
38
+ | No added latency | Evaluation is eager on the edge; the tick services only time (§2) |
39
+
40
+ Two defects in the current systems that this fixes on the way past:
41
+
42
+ 1. **Binding resolution can throw.** `resolvePath` throws on an unresolvable path
43
+ ([`resolvePathByArray.js:47`](../../../../core/json/resolvePathByArray.js)), which is why a gamepad binding
44
+ is a load-order landmine (see [`GAMEPAD_API_PROPOSAL.md §3`](../../GAMEPAD_API_PROPOSAL.md)). Source
45
+ resolution here is total.
46
+ 2. **Nothing can express a combination.** `Ctrl+Z` is not expressible as a path, which is why
47
+ [`EditorKeyMap`](../../../../../editor/EditorKeyMap.js) exists as a parallel, unserializable, unrebindable
48
+ input system made of `if (event.ctrlKey)`.
49
+
50
+ ---
51
+
52
+ ## 1. Scope
53
+
54
+ ### 1.1 Pointer gestures are out of scope
55
+
56
+ `PointerDevice` synthesizes gestures on top of its buttons — `tap`, `drag`, `dragStart`, `dragEnd`, `move`,
57
+ `wheel`, `pinch`, `pinchStart`, `pinchEnd` — and those are **not** modelled here. They are stateful
58
+ constructions with their own parameters (`tap` fires on *release*, gated on ≤ 10 px of travel and ≤ 1 s;
59
+ `PointerDevice.js:20-40`), not switches, and folding them into the trigger algebra is a separate piece of work.
60
+
61
+ This is a deliberate limit with a measurable cost. Of the pointer bindings in shipping consumers:
62
+
63
+ | `down`/`up` | `tap` | `drag`/`dragStart`/`dragEnd` | `move` | `wheel` | `pinch`/`pinchStart` |
64
+ |---|---|---|---|---|---|
65
+ | 6 | 4 | 4 | 2 | 2 | 2 |
66
+
67
+ Only the first column is expressible. **Consumers bound to gestures stay on `InputController`**, which is
68
+ therefore *reduced*, not retired, by this program (§14). Chief among them:
69
+ `StrategyInteractionController` (17 bindings, including both pinch bindings),
70
+ `makeOrbitalCameraController` (tap + down + drag + wheel), and `RadialContextMenu`.
71
+
72
+ ### 1.2 What is replaced
73
+
74
+ | Mechanism | Disposition | Why |
75
+ |---|---|---|
76
+ | [`Input`](../components/Input.js) + [`InputSystem`](../systems/InputSystem.js) | **Retired** (one holdout, §14) | Magic string paths; one source per binding; no combinations; `resolvePath` throws; `serializable = false` |
77
+ | [`EditorKeyMap`](../../../../../editor/EditorKeyMap.js) | **Retired** | Exists only because chords are inexpressible above; hard-coded, unrebindable, undiscoverable |
78
+ | `Tool.handleKeyboardEvent` | **Retired** | An unconditional forward to the active tool, with no gating |
79
+ | [`KeyboardCameraController`](../controllers/KeyboardCameraController.js) | **Retired** | 16 `bind()` calls maintaining a `controls` struct — a 2-D coordinate written by hand (§3.3) |
80
+ | [`InputController`](../components/InputController.js) + [`InputControllerSystem`](../systems/InputControllerSystem.js) | **Reduced** | Keyboard and button bindings move; gesture bindings stay (§1.1) |
81
+ | Direct `engine.devices.*.on.*.add(...)` in gameplay code | **Case by case** | Not bindable, not rebindable, not occludable; but gesture subscriptions have nowhere to go yet |
82
+
83
+ **Not in scope at all:** direct device reads in `prototype*.js` / `test*.js` harnesses. Those are internal
84
+ tools, and polling `pad.buttons[GamepadButtons.a].is_down` remains the blessed path for anything that does not
85
+ want a binding layer. The ISM sits *above* the devices; it does not close them off.
86
+
87
+ ---
88
+
89
+ ## 2. Evaluation: eager on edges, ticked only for time
90
+
91
+ There is **no input buffer and no added frame of latency.** An edge is evaluated and dispatched inside the
92
+ device signal that produced it.
93
+
94
+ ```
95
+ InputDeviceSwitch.down / .up (keyboard/pointer: DOM event. gamepad: poll.)
96
+ |
97
+ v
98
+ ISM edge handler --- synchronous, no queue ---------------------------------
99
+ |
100
+ | 1. stamp arrival time
101
+ | 2. update #held : BitSet
102
+ | 3. append to the recorder, if one is attached (§2.4)
103
+ | 4. walk maps by `order` descending, skipping suppressed maps (§8):
104
+ | for each candidate trigger touching this switch,
105
+ | in specificity order (§4.6):
106
+ | was := trigger.satisfied
107
+ | now := trigger.test(#held)
108
+ | trigger.satisfied := now
109
+ | if (now && !was) consume, dispatch
110
+ | 5. apply changes deferred during dispatch (§2.3)
111
+ v
112
+ dataset.sendEvent(entity, action, event) -- consumer runs here, this instant
113
+ ```
114
+
115
+ `#held` is exact at dispatch time by construction, because dispatch *is* the edge. No replay, no
116
+ reconstruction.
117
+
118
+ ### 2.1 The tick services time, and only time
119
+
120
+ `System.update(dt)` dispatches no edge-driven action. It services the four things that change without an edge:
121
+
122
+ 1. **`InputTriggerHold` maturity.** A hold for 0.5 s becomes true 0.5 s after its child did, which no edge
123
+ announces. When it matures it emits a **synthetic edge** into the same walk as step 4 above, carrying the
124
+ hold's own `collectSources()` set so consumption (§8) has something to match against. Latency is bounded by
125
+ the frame, which is inherent: nothing observes time more finely than the loop observing time.
126
+ 2. **Sequence step windows** (§5.2) — both the refresh while a previous step still holds, and the expiry.
127
+ 3. **`Delta` coordinate reset** (§3.3).
128
+ 4. **Suppression recompute** when the linked map set, `order` or `occlusion` changed during the frame (§8).
129
+
130
+ A trigger built only from switches and chords is untouched by `update`.
131
+
132
+ ### 2.2 Polled devices are bounded by their own poll rate
133
+
134
+ `GamepadDevice` polls once per animation frame and drives its switches from the result
135
+ (`GamepadDevice.js:370-407`), so gamepad edges cannot be finer-grained than that — a Gamepad API property, not
136
+ one this design imposes. `GamepadDevice.poll()` is callable manually, so a consumer driving a fixed-rate loop
137
+ can raise its own resolution. Keyboard and pointer edges are DOM-driven and carry no such floor.
138
+
139
+ ### 2.3 The re-entrancy contract
140
+
141
+ Consumer code runs inside a DOM event handler. Four cases, each with a stated rule:
142
+
143
+ * **Structural change** — a handler adds or removes an entity, and therefore an `InputMap`. Queued, applied at
144
+ step 5, after the current edge finishes.
145
+ * **Binding edits on a linked map** — a handler calls `bind()` / `pushLayer()`, which §9.3 says triggers a
146
+ recompile. The recompile is queued with structural changes and applied at step 5; the walk always runs to
147
+ completion against the program it started with. This path is not exotic: it is the rebinding-capture flow.
148
+ * **Nested edges** — a handler calls `GamepadDevice.poll()` (blessed in §2.2) or synchronously dispatches a DOM
149
+ event (`element.click()`, `.focus()`), re-entering the walk. **Rejected**: a re-entrant edge is queued and
150
+ replayed at step 5 rather than processed inline, because processing it inline would mutate `#held` under the
151
+ outer walk and clobber the shared `InputActionEvent` (§6.3) before the outer handler finished reading it.
152
+ * **A handler that throws** — it throws. `#held` and the recorder are updated *before* dispatch (steps 2–3), so
153
+ ISM state stays consistent and the failure is visible, per the codebase's position on not swallowing errors.
154
+
155
+ *(For contrast, `InputControllerSystem` carries a `deferred` array for the first case that **nothing ever
156
+ populates** — `add()` registers directly under a `//TODO handle case where binding is added mid-dispatch`
157
+ (`InputControllerSystem.js:119-121`). The hazard is real and currently unhandled, not handled badly.)*
158
+
159
+ ### 2.4 Ordering, and what it costs
160
+
161
+ Action effects land wherever in the frame the edge arrived, so two actions in one frame are ordered by
162
+ arrival, not by system order. That is the direct consequence of wanting zero latency, and §4.6 and §5.5 record
163
+ the two places it is observable.
164
+
165
+ ### 2.5 Recording and replay
166
+
167
+ An optional recorder captures, per frame: the frame's `dt`, every switch edge as
168
+ `(source_index, is_down, time)`, and a sample of every **coordinate channel** any linked map references.
169
+ Coordinates must be recorded because they are read by polling, not driven by edges — without them,
170
+ `InputActionEvent.value` and every `axis*` read diverge on replay. `dt` must be recorded because hold maturity
171
+ and window expiry are evaluated on the tick, so a different frame cadence produces different fire times.
172
+
173
+ Replay injects recorded edges at their recorded positions within each recorded frame and drives the same eager
174
+ path. Interned `source_index` values are stable only within one link generation, so the recorder writes the
175
+ source table alongside and remaps on load.
176
+
177
+ The recorder is an observer, not the mechanism — attach it for a determinism run or a bug report, leave it off
178
+ otherwise.
179
+
180
+ ### 2.6 `preventDefault`, and its granularity
181
+
182
+ [`KeyboardDevice`](../../devices/KeyboardDevice.js) calls `event.preventDefault()` when the pressed key's
183
+ `down` signal *has handlers* (`KeyboardDevice.js:112-118`). Since the ISM subscribes to precisely the keys some
184
+ live binding references, browser-default suppression tracks the bound key set automatically.
185
+
186
+ **Granularity is per key, not per chord**, and this is worth knowing before it surprises someone: binding only
187
+ `Ctrl+S` subscribes `s`'s `down` signal, so a *bare* `S` keydown is also `preventDefault`ed anywhere the device
188
+ element sees it. This is inherited behaviour, not new — `InputController` binding `keyboard/keys/s/down` does
189
+ the same — but chords make it newly reachable. Changing it means changing `KeyboardDevice`, which is out of
190
+ scope here.
191
+
192
+ ---
193
+
194
+ ## 3. Devices, switches and coordinates
195
+
196
+ ### 3.1 The adapter
197
+
198
+ ```js
199
+ /**
200
+ * Adapts a concrete input device to the ISM.
201
+ *
202
+ * Resolution must be TOTAL: an adapter answers for every code it declares, whether or not hardware is
203
+ * present. Absent hardware reads as "up" and as zero, never as undefined, and never throws. This is the
204
+ * property `resolvePath` lacked.
205
+ */
206
+ export class InputDeviceAdapter {
207
+ /** Stable identifier, written into serialized bindings. ASCII, lower_snake_case. @type {string} */
208
+ id = "";
209
+
210
+ /** @param {number} code @returns {InputDeviceSwitch} */
211
+ getSwitch(code) { }
212
+
213
+ /** @param {number[]|Float64Array} result @param {number} offset @param {number} code */
214
+ readCoordinate(result, offset, code) { }
215
+
216
+ /** Begin/stop observing the device. Adapters with nothing to subscribe to leave these alone. */
217
+ startup() { }
218
+ shutdown() { }
219
+
220
+ /** Publish this frame's relative-channel totals and start a new window. See the latch rule in §3.3. */
221
+ latchDeltas() { }
222
+ }
223
+ ```
224
+
225
+ A channel's *dimensionality* is not asked of the adapter. It is a static property of the channel code, known
226
+ to the typed constructors (§3.3), so a binding on an unregistered device still knows its own shape.
227
+
228
+ | Adapter | `id` | Switch codes | Coordinate codes |
229
+ |---|---|---|---|
230
+ | `KeyboardInputDeviceAdapter` | `"keyboard"` | [`KeyCodes`](../../devices/KeyCodes.js) | — |
231
+ | `PointerInputDeviceAdapter` | `"pointer"` | `MouseButtons` | `PointerCoordinates` |
232
+ | `GamepadInputDeviceAdapter` | `"gamepad"` | [`GamepadButtons`](../../devices/gamepad/GamepadButtons.js) | `GamepadCoordinates` |
233
+
234
+ Three enums are new, and each needs a stability note because their values become `uint16` codes on disk:
235
+
236
+ * **`MouseButtons`** — `left: 0, middle: 1, right: 2, back: 3, forward: 4`. These are
237
+ **`MouseEvent.button` ordinals**, matching how `PointerDevice.buttons[]` is indexed
238
+ (`PointerDevice.js:300-318`: `mouseButtonLeft` is `[0]`, `mouseButtonMiddle` is `[1]`, `mouseButtonRight` is
239
+ `[2]`). They are deliberately **not** the `MouseEvent.buttons` *bitmask* order, which is left/right/middle —
240
+ taking that order would index `buttons[1]` for a right-click and fire every right-click binding on the
241
+ middle button, into saved data.
242
+ * **`PointerCoordinates`** — `position`, `move`, `wheel`.
243
+ * **`GamepadCoordinates`** — `left_stick`, `right_stick`, `dpad`.
244
+
245
+ Values are append-only once shipped, like `KeyCodes` and `GamepadButtons` before them.
246
+
247
+ A fourth device — MIDI pedal, wheel, eye tracker, a network-driven replay source — is an adapter and a
248
+ registration call. Nothing in the trigger, layer, cascade or serialization layers changes, because none of them
249
+ can tell devices apart.
250
+
251
+ The gamepad adapter binds `GamepadDevice.main`, the persistent handle: it exists before any hardware does,
252
+ survives disconnection, and rebinds to the oldest connected pad on a swap (`GamepadDevice.js:110, 306-344`). A
253
+ serialized gamepad binding therefore resolves at load time and keeps working across a hot-swap — the defect
254
+ `GAMEPAD_API_PROPOSAL.md §3` identified and could not fix from the device side alone.
255
+
256
+ **A device that is not registered** is not an error. A map naming an unregistered `id` links successfully and
257
+ its bindings on that device are inert — the same contract as connected-but-absent hardware, so a saved map
258
+ never fails to load because a peripheral is missing. `registerDevice()` may be called after maps are linked; it
259
+ recompiles every linked map (§9.3), at which point those bindings become live.
260
+
261
+ ### 3.2 Switch sources
262
+
263
+ ```js
264
+ export class InputSource {
265
+ device = ""; // InputDeviceAdapter#id
266
+ code = 0;
267
+ }
268
+ ```
269
+
270
+ One flat value type with `equals`/`hash`/`toBinaryBuffer`/`fromBinaryBuffer`, deliberately *not* a
271
+ `KeySource`/`GamepadButtonSource`/… hierarchy: a chord over `{keyboard:ctrl, pointer:0}` needs no polymorphic
272
+ dispatch, serialization is `(device_index: Uint8, code: Uint16)` against a per-map device table (§10) with no
273
+ type tags, and interning to an integer (§9.3) is a two-field hash after which the hot path never touches the
274
+ object again. The typing that matters to an author lives on the *trigger* constructors (§4.2).
275
+
276
+ ### 3.3 Coordinates — analog channels of arbitrary dimensionality
277
+
278
+ An analog channel is not a scalar axis. `WheelEvent` carries `deltaX`, `deltaY` and `deltaZ`, and
279
+ [`PointerDevice`](../../devices/PointerDevice.js) surfaces all three (`PointerDevice.js:398-404`). A 1-D axis
280
+ abstraction would have to either drop `deltaZ` or invent three channels for one control.
281
+
282
+ *(Precisely: `PointerDevice` reports `sign(event.deltaX/Y/Z)`, so components are -1, 0 or 1 and scroll
283
+ magnitude is discarded at the device. A `Delta` wheel channel therefore accumulates **tick counts**, not
284
+ distance. That is the device's existing behaviour; the ISM reports what it is given.)*
285
+
286
+ ```js
287
+ export class InputCoordinateSource {
288
+ device = "";
289
+ code = 0;
290
+ /** Component count. 2 for a stick or pointer position, 3 for a wheel. @type {number} */
291
+ dimensions = 1;
292
+ /** @type {InputCoordinateKind} */
293
+ kind = InputCoordinateKind.Normalized;
294
+ }
295
+
296
+ export const InputCoordinateKind = {
297
+ /** A position in some space. Read at any time, no accumulation. Pointer position. */
298
+ Absolute: 0,
299
+ /** A change. Accumulates within a frame, zeroed at the frame boundary. Wheel. */
300
+ Delta: 1,
301
+ /** Deflection, each component in [-1, 1]. Stick, d-pad. */
302
+ Normalized: 2
303
+ };
304
+ ```
305
+
306
+ `kind` is the verb — what the numbers *mean*. `Delta` is the one with behaviour attached, and it is why the
307
+ tick exists at all: two wheel events in one frame must sum.
308
+
309
+ **The `Delta` visibility rule, stated exactly.** A relative channel **latches**; it is not cleared. The
310
+ adapter accumulates arriving events into one buffer, and once per frame `update` publishes that buffer into a
311
+ second one and starts a fresh accumulation window. Readers see the published value.
312
+
313
+ Latching rather than clearing is load-bearing, and the obvious alternative is wrong. Device events arrive
314
+ *between* frames, so an adapter that zeroed its accumulator at the top of a frame would destroy exactly the
315
+ events that frame exists to report. Latching also makes the value **independent of system execution order**:
316
+ every reader in a frame sees the same number whether it runs before or after the ISM, which is what the
317
+ "same value for every reader" guarantee actually requires.
318
+
319
+ An **eager handler** reading a `Delta` channel mid-frame sees the value published for the frame in progress —
320
+ the same one the tick-time readers will see — not a running total of events that have arrived since. Those
321
+ belong to the next frame.
322
+
323
+ **Reading.** The general accessor writes into an indexable numeric buffer; common dimensionalities get typed
324
+ conveniences over the engine's vector types:
325
+
326
+ ```js
327
+ map.coordinate(result, offset, 'look'); // Float64Array | number[] - any dimensionality
328
+ map.axis1(v1, 'throttle'); // Vector1
329
+ map.axis2(v2, 'move'); // Vector2
330
+ map.axis3(v3, 'wheel'); // Vector3
331
+ ```
332
+
333
+ Result-first argument order follows the engine's `f(result, ...)` convention.
334
+
335
+ **Channels.** A coordinate action is driven by a channel, and the consumer cannot tell which kind:
336
+
337
+ ```js
338
+ InputCoordinateGamepad.from(GamepadCoordinates.left_stick) // hardware channel
339
+ InputCoordinatePointer.from(PointerCoordinates.wheel) // hardware channel, 3-D
340
+ InputCoordinateSwitches.from([ // N-D from 2N switch triggers
341
+ InputTriggerKey.from('a'), InputTriggerKey.from('d'), // x: negative, positive
342
+ InputTriggerKey.from('w'), InputTriggerKey.from('s') // y: negative, positive
343
+ ])
344
+ InputCoordinateCompose.from(x_channel, y_channel) // stitch channels
345
+ ```
346
+
347
+ `InputCoordinateSwitches` is [`KeyboardCameraController`](../controllers/KeyboardCameraController.js) as data,
348
+ and it generalises past 2-D for free.
349
+
350
+ **Shaping stays at the call site.** No dead zone, no response curve, no sensitivity inside the ISM. It returns
351
+ the composed raw value and the consumer calls
352
+ [`apply_radial_dead_zone`](../../analog/apply_radial_dead_zone.js). This is the position
353
+ `GAMEPAD_API_PROPOSAL.md §1.2` argues at length; reversing it here would put the same policy back in the same
354
+ wrong layer, one floor up.
355
+
356
+ ### 3.4 Derived switches: a direction is a coordinate with a threshold
357
+
358
+ `InputTriggerDirection` turns a coordinate into a switch — "the stick is in the *down-right* octant". This is
359
+ what makes motion inputs work (§5.4), and it is not bolted on for them: a d-pad is already a coordinate in this
360
+ codebase (`gamepad_write_dpad_vector`), so one path serves stick and d-pad.
361
+
362
+ The eight directional switches derived from one coordinate are **mutually exclusive by construction**, and the
363
+ derived layer **releases the old direction before pressing the new one**, so exclusivity is never momentarily
364
+ violated. §5.4 depends on both.
365
+
366
+ Three properties an implementer must know, because none is free:
367
+
368
+ * **Threshold.** Producing a boolean from a coordinate requires one by definition. This is not an exception to
369
+ §3.3, which forbids shaping the *analog value* handed to a consumer. Constructor argument, defaulted.
370
+ * **Sampling.** Octant transitions are observed only as often as the channel updates. On a gamepad that is the
371
+ rAF poll (§2.2), so a fast quarter-circle can pass from `Down` to `Right` between two polls without ever
372
+ reporting `DownRight`, and a motion requiring that step silently fails. Sequences over directions are
373
+ reliable at hand speed, not at frame-perfect speed.
374
+ * **Hysteresis.** A stick resting on an octant boundary chatters release/press pairs. The threshold carries an
375
+ angular hysteresis band, defaulted, for the same reason it carries a magnitude threshold: without it a
376
+ boundary rest position resets hold timers continuously.
377
+
378
+ ---
379
+
380
+ ## 4. Triggers
381
+
382
+ ### 4.1 One interface, two methods and one field
383
+
384
+ ```js
385
+ export class InputTrigger {
386
+ /**
387
+ * Is this trigger's condition currently met?
388
+ * Pure for leaves and combinators. `InputTriggerHold` and `InputTriggerSequence` additionally consult
389
+ * per-instance state, which edges and ticks maintain.
390
+ *
391
+ * @param {BitSet} held interned switch indices currently down
392
+ * @returns {boolean}
393
+ */
394
+ test(held) { }
395
+
396
+ /**
397
+ * Every switch index this trigger can be affected by. Collected once, at compile time (§9.3).
398
+ * @param {number[]} result
399
+ */
400
+ collectSources(result) { }
401
+
402
+ /**
403
+ * Last observed value of `test`, per compiled instance. A binding fires when `test` is true and this
404
+ * is false. Initialised at compile time to `test(held-at-compile)` - NOT to false. See §4.5.
405
+ * @type {boolean}
406
+ */
407
+ satisfied = false;
408
+ }
409
+ ```
410
+
411
+ **A binding fires on the rising edge of `test`.** That single rule covers press, release, hold, chord, sequence
412
+ and direction, and it is why there is no `Started`/`Performed`/`Canceled` phase enum and no `edge` field.
413
+
414
+ ### 4.2 Leaf triggers — typed constructors
415
+
416
+ ```js
417
+ InputTriggerKey.from('space')
418
+ InputTriggerKey.from(KeyCodes.space)
419
+ InputTriggerMouseButton.from(MouseButtons.left)
420
+ InputTriggerGamepadButton.from(GamepadButtons.a)
421
+ InputTriggerDirection.from(stick, InputDirection.DownRight) // stick: any InputCoordinate* channel
422
+ ```
423
+
424
+ Each extends `InputTriggerSwitch`, which holds one `InputSource` and whose `test` is `held.get(index)`. The
425
+ subclasses add no fields — they are typed, validating constructors that fix the device and check the code
426
+ against that device's enum. `InputTriggerKey.from('nonexistent')` fails at authoring time under `assert.*`,
427
+ which is compiled out of production, so the typing is free in shipped code.
428
+
429
+ Serialization round-trips the flat form, so a deserialized trigger is an `InputTriggerSwitch`, not an
430
+ `InputTriggerKey`. Nothing needs the subclass identity at runtime — it exists for the author and the assertion.
431
+
432
+ ### 4.3 Combinators
433
+
434
+ ```js
435
+ InputTriggerChord.from(a, b, c) // test = AND over children
436
+ InputTriggerRelease.from(a) // test = NOT child.test
437
+ InputTriggerHold.from(a, 0.5) // test = child.test AND continuously true for >= 0.5s
438
+ InputTriggerSequence.from(a, b, c) // test = the matcher completed on this edge (§5)
439
+ ```
440
+
441
+ Combinators take *triggers*, not sources, which is why the requirements compose:
442
+
443
+ ```js
444
+ // "the chord ended"
445
+ InputTriggerRelease.from(InputTriggerChord.from(InputTriggerKey.from('ctrl'),
446
+ InputTriggerKey.from('z')))
447
+
448
+ // "hold Ctrl+Shift for half a second"
449
+ InputTriggerHold.from(InputTriggerChord.from(InputTriggerKey.from('ctrl'),
450
+ InputTriggerKey.from('shift')), 0.5)
451
+
452
+ // cross-device: nothing here knows a device is involved
453
+ InputTriggerChord.from(InputTriggerKey.from('ctrl'),
454
+ InputTriggerMouseButton.from(MouseButtons.left))
455
+ ```
456
+
457
+ `InputTriggerRelease` as *negation* rather than an edge kind is what makes it compose — an edge flag on a chord
458
+ could not express "when this chord stops holding".
459
+
460
+ **One composition that does not mean what it looks like:** `InputTriggerRelease.from(sequence)`. A sequence's
461
+ `test` is a single-edge pulse (§5.2 step 5), so negating it yields "true except on the completing edge", which
462
+ rises on the *next* evaluated edge after every completion — an echo of the fire, not "the sequence stopped
463
+ matching". A sequence's partial-match state is deliberately not exposed to `test`. Do not wrap a sequence in
464
+ `Release`.
465
+
466
+ ### 4.4 Step timeout lives on the sequence
467
+
468
+ ```js
469
+ InputTriggerSequence.from(a, b, c).withStepTimeout(0.25)
470
+ ```
471
+
472
+ Default **1.0 s**, which suits editor and menu sequences whose first step is a chord the author must release
473
+ before reaching for the next key. Fighting-game motions want 0.15–0.25 s. §5.2 defines exactly what the window
474
+ measures, which is not what a naive reading suggests.
475
+
476
+ Timing was specified here as **real time**, stamped at edge arrival, on the argument that a paused game must
477
+ not give a chord an infinite window and a game at 0.25× speed must not quarter the player's reflexes.
478
+
479
+ **Superseded.** As built it is *simulation* time, advanced by `update(dt)` and nowhere else. The reason is
480
+ testability: a real-time clock cannot be driven from a spec without faking one, and faking it is a mock. The
481
+ consequences are real and accepted — windows stretch under slow motion and freeze while the game is paused,
482
+ so a hold inside a pause menu can never mature. See the implementation plan's amendments section for the full
483
+ argument.
484
+
485
+ ### 4.5 `satisfied` is initialised from state, not from false
486
+
487
+ At compile time each trigger instance sets `satisfied := test(held)` against the currently held switches. It is
488
+ **not** zero-initialised, and the difference is not academic:
489
+ `InputTriggerRelease.from(InputTriggerChord.from(ctrl, z))` has `test` true whenever the chord is *not* held —
490
+ which is almost always. Zero-initialised, the first edge touching `ctrl` or `z` would see `now=true, was=false`
491
+ and fire "the chord ended" before the chord ever existed.
492
+
493
+ This matters on every compile, not just the first: maps link mid-dispatch (§2.3), and `registerDevice` and
494
+ binding edits both recompile live maps while keys are held.
495
+
496
+ ### 4.6 Precedence within a layer
497
+
498
+ Candidate triggers on one edge are ordered, and the order is total so it never depends on declaration accident:
499
+
500
+ 1. **Sequences first**, longer before shorter (by step count).
501
+ 2. Then by **chord width** — the number of switches that must be simultaneously held. `Hold` and `Release`
502
+ rank by their child's width, so `Hold(Chord(a,b), t)` ranks as width 2 and beats `Key(a)`; `Direction` ranks
503
+ as width 1.
504
+ 3. Then declaration order within the layer.
505
+
506
+ A fired trigger consumes its switches for the remainder of the current edge (§8), so `Ctrl+Z` firing means `Z`
507
+ does not.
508
+
509
+ **The condition on that guarantee.** Consumption is scoped to one edge, so it arbitrates only triggers that
510
+ complete on the *same* edge. `Ctrl` pressed before `Z` gives one edge (`z↓`) at which both `Ctrl+Z` and `Z` are
511
+ satisfiable, and the chord wins. `Z` pressed **before** `Ctrl` gives two edges: `z↓` fires `Z`'s action (the
512
+ chord is not yet satisfiable, so there is nothing to lose to), then `ctrl↓` fires `undo`. **Both fire.**
513
+
514
+ This is inherent to zero-latency evaluation — suppressing the first would mean either delaying it or retracting
515
+ a dispatched action, and both are excluded. Practically it is rarely reached, because modifier-first is how
516
+ people type chords. Where it matters, do not bind a bare key in the same layer as a chord that contains it.
517
+
518
+ ---
519
+
520
+ ## 5. Sequence semantics
521
+
522
+ ### 5.1 State
523
+
524
+ Per sequence binding instance, three numbers, held on the **compiled clone** of the trigger (§9.3):
525
+
526
+ ```js
527
+ #step // steps matched so far, 0..N
528
+ #stepSatisfied // last observed value of steps[#step].test
529
+ #window // time the current step's window last refreshed
530
+ ```
531
+
532
+ *(An earlier revision put these in typed arrays on the program, indexed by sequence-binding index. Cloning per
533
+ binding is what the compiler does anyway — authored triggers are shared, so evaluation state cannot live on
534
+ them — and once each binding owns a private trigger object, three fields on it are the same thing with less
535
+ bookkeeping. Nothing allocates per evaluation either way.)*
536
+
537
+ Entering a state is one operation, and **every** path into a state uses it. That is the whole of §17's most
538
+ important correction:
539
+
540
+ ```js
541
+ function enter(k, held, now) {
542
+ step = k;
543
+ satisfied = (k < N) ? steps[k].test(held) : false; // the entry snapshot
544
+ window = now;
545
+ }
546
+ ```
547
+
548
+ ### 5.2 The rule
549
+
550
+ Evaluated at each edge (device or synthetic) and each tick:
551
+
552
+ ```
553
+ 1. if (step > 0) {
554
+ if (steps[step - 1].test(held)) window := now // previous step still holds
555
+ else if (now - window > step_timeout) enter(0, held, now) // window expired
556
+ }
557
+
558
+ 2. cur := steps[step].test(held)
559
+
560
+ 3. if (cur && !satisfied) {
561
+ enter(step + 1, held, now)
562
+ if (step === N) {
563
+ fire() // the sequence's own `test` reads true for this edge
564
+ enter(0, held, now)
565
+ }
566
+ } else {
567
+ satisfied := cur
568
+ }
569
+ ```
570
+
571
+ Three properties do all the work:
572
+
573
+ * **Advance on a rising edge, not on satisfaction.** Otherwise one long press of `A` satisfies both steps
574
+ of `[A, A]`.
575
+ * **The entry snapshot, on every entry.** Advance, expiry and post-fire wrap all route through `enter`, so a
576
+ reset can never leave a stale `satisfied` behind. `enter` never indexes `steps[N]`.
577
+ * **The window measures time since the previous step stopped holding**, not time since the advance. Step 1
578
+ refreshes `window` for as long as `steps[step-1]` is still satisfied, which is what makes charge inputs work
579
+ (§5.4) and is why the tick must service sequences even when no edge arrives.
580
+
581
+ **A mismatch does not reset. Only window expiry resets.** A sequence is a leniency window, and the window is
582
+ what §5.2 step 1 defines.
583
+
584
+ ### 5.3 Worked: `[A, A, B]` on `A↓ A↑ A↓ A↑ A↓ B↓`
585
+
586
+ | edge | held | step | step-1 holds? | `cur` | `satisfied` | result |
587
+ |---|---|---|---|---|---|---|
588
+ | — | {} | 0 | — | — | false (compile snapshot) | |
589
+ | `A↓` | {A} | 0 | — | true | false | **rise** → `enter(1)`, `satisfied := A.test({A}) = true` |
590
+ | `A↑` | {} | 1 | no | false | true | `satisfied := false` |
591
+ | `A↓` | {A} | 1 | yes → refresh | true | false | **rise** → `enter(2)`, `satisfied := B.test({A}) = false` |
592
+ | `A↑` | {} | 2 | no | false | false | — |
593
+ | `A↓` | {A} | 2 | yes → refresh | false | false | — |
594
+ | `B↓` | {A,B} | 2 | yes → refresh | true | false | **rise** → `enter(3)` = N → **fire**, `enter(0)` |
595
+
596
+ No failure function, no KMP: the entry snapshot alone prevents one press from satisfying two steps.
597
+
598
+ **Post-fire, the case revision 2 got wrong.** `enter(0, {A,B}, now)` snapshots `satisfied := A.test({A,B}) =
599
+ true`. So with `A` still held, tapping `B` again gives `cur = true, satisfied = true` — no rise, no fire.
600
+ Revision 2 left `satisfied` untouched on the post-fire reset, and holding `A` while mashing `B` machine-gunned
601
+ the action off a single `A` press.
602
+
603
+ ### 5.4 Worked: chords, motions and charge
604
+
605
+ **`Ctrl+Shift+A > B`** — steps are triggers, and a chord is a trigger, so this is not a special case:
606
+
607
+ ```js
608
+ map.bind('promote', InputTriggerSequence.from(
609
+ InputTriggerChord.from(InputTriggerKey.from('ctrl'),
610
+ InputTriggerKey.from('shift'),
611
+ InputTriggerKey.from('a')),
612
+ InputTriggerKey.from('b')
613
+ ));
614
+ ```
615
+
616
+ `ctrl↓`, `shift↓`: chord false. `a↓`: chord rises → `enter(1)`, `satisfied := b.test({ctrl,shift,a}) = false`.
617
+ Then `b↓` fires — **whether or not the modifiers were released first**, because leaf and chord tests are
618
+ *subset* tests over the held set, and because holding the chord keeps refreshing the window rather than
619
+ starting its clock.
620
+
621
+ **Quarter-circle forward, `↓ ↘ →`** — this is the case that only works because directions are derived from a
622
+ coordinate (§3.4) rather than read off raw d-pad buttons:
623
+
624
+ ```js
625
+ const stick = InputCoordinateGamepad.from(GamepadCoordinates.left_stick);
626
+
627
+ map.bind('hadouken', InputTriggerSequence.from(
628
+ InputTriggerDirection.from(stick, InputDirection.Down),
629
+ InputTriggerDirection.from(stick, InputDirection.DownRight),
630
+ InputTriggerDirection.from(stick, InputDirection.Right)
631
+ ).withStepTimeout(0.2));
632
+ ```
633
+
634
+ | edge | held | step | `cur` | result |
635
+ |---|---|---|---|---|
636
+ | `Down↓` | {Down} | 0 | true | **rise** → `enter(1)`, `satisfied := DownRight.test({Down}) = false` |
637
+ | `Down↑` | {} | 1 | false | — |
638
+ | `DownRight↓` | {DownRight} | 1 | true | **rise** → `enter(2)`, `satisfied := Right.test({DownRight}) = false` |
639
+ | `DownRight↑` | {} | 2 | false | — |
640
+ | `Right↓` | {Right} | 2 | true | **rise** → `enter(3)` = N → **fire** |
641
+
642
+ The two snapshots in the result column are the trick. With raw d-pad buttons `↓` and `→` can be held together,
643
+ `Right.test({Down,Right})` is `true` on entry to state 2, and the motion can never complete. Exclusivity by
644
+ construction is what removes that failure mode — which is why §12.2 tests it in both directions.
645
+
646
+ **Dragon punch, `→ ↓ ↘`** — the same trace with steps permuted; each entry snapshot is false for the same
647
+ reason. Verified also against the realistic rolled input `Right, DownRight, Down, DownRight`, where the
648
+ intermediate `DownRight↓` at step 1 is a non-advancing mismatch (ignored, correctly) and the final one fires.
649
+
650
+ **Charge, `hold ← for 2 s, then →`** — the case revision 2 published as passing and which in fact failed:
651
+
652
+ ```js
653
+ InputTriggerSequence.from(
654
+ InputTriggerHold.from(InputTriggerDirection.from(stick, InputDirection.Left), 2.0),
655
+ InputTriggerDirection.from(stick, InputDirection.Right)
656
+ )
657
+ ```
658
+
659
+ At t=2.0 the hold matures, emits a synthetic edge (§2.1) and rises → `enter(1)`. The player then keeps holding
660
+ `←`, which is what charge play *is*. Under revision 2 the window ran from the advance, so any charge longer
661
+ than `duration + step_timeout` expired before the flick and the move was silently lost; raising the timeout
662
+ instead produced ghost charges minutes later. Under §5.2 step 1, `steps[0].test` — the hold — is still true
663
+ while `←` is held, so `window` is refreshed on every tick. Release `←` at t=8.0 and the window opens *there*;
664
+ `→` within `step_timeout` fires. Return to neutral instead and the window expires normally, so there is no
665
+ ghost.
666
+
667
+ **Motion plus button, `236 + P`** — append `InputTriggerGamepadButton.from(GamepadButtons.x)` as a fourth step.
668
+ While `→` is held, step 1 keeps refreshing the window, so the button may be pressed at leisure; it fires with
669
+ `→` still held because button steps are subset tests.
670
+
671
+ ### 5.5 What this deliberately cannot do
672
+
673
+ **Strict rejection.** `↓ ← →` inside the window fires the quarter-circle, because `←` neither advances nor
674
+ resets. Arcade-grade matchers reject a direction outside the expected set. The shape of the fix is known — an
675
+ optional rejection predicate on the sequence, resetting to 0 when it matches — and it is **deferred until a
676
+ consumer asks**, on the position that the common case should be the only case. For every sequence in this
677
+ codebase, and for menu, cheat-code and editor cases, leniency is the wanted behaviour.
678
+
679
+ **Button buffering.** A button pressed *before* the motion step it follows poisons that step's entry snapshot
680
+ and must be re-pressed. Genre-standard buffering is a fighting-game engine feature, not an input-mapping one.
681
+
682
+ **Tap versus hold on the same switch — accepted as a cost of zero latency.** Given
683
+ `'interact' = Key(e)` and `'loot_all' = Hold(Key(e), 0.5)`, a press-and-hold fires **both**: `'interact'` at
684
+ `e↓`, `'loot_all'` 0.5 s later. They complete on different edges, so §4.6's per-edge consumption cannot
685
+ arbitrate them, and no composition inside the algebra separates them either —
686
+ `Chord(Release(e), Release(Hold(e, 0.5)))` collapses, because `hold.test` falls to false on the same `e↑` edge
687
+ the release rises on.
688
+
689
+ Discriminating them requires *withholding* the short action until the hold window has passed, which is
690
+ precisely the frame-buffered dispatch this design exists to avoid. Paying that architectural cost — a second,
691
+ deferred dispatch path with its own ordering and occlusion semantics — for one interaction pattern is not
692
+ worth it. **Tap-and-hold pairs must use distinct inputs** (`E` and `Shift+E`), or accept that the hold is
693
+ additive to the tap. Revision 2 claimed §4.6 arbitrated this; it does not.
694
+
695
+ **Key autorepeat.** `KeyboardDevice` discards `event.repeat` (`KeyboardDevice.js:93-97`) and
696
+ `InputDeviceSwitch.press()` is idempotent, so a held key produces exactly one edge. Autorepeat is not
697
+ observable anywhere in this design, and a consumer that wants repeat behaviour — held-key menu navigation,
698
+ `EditorKeyMap`'s current repeat-undo — must drive it from `isActive` plus its own timer. Changing this means
699
+ changing `KeyboardDevice`, which is out of scope.
700
+
701
+ ### 5.6 Why `SimpleStateMachine` is not used
702
+
703
+ The first revision built this on
704
+ [`SimpleStateMachine`](../../../../core/fsm/simple/SimpleStateMachine.js) and
705
+ [`SimpleStateMachineDescription`](../../../../core/fsm/simple/SimpleStateMachineDescription.js). It does not
706
+ earn its place here, and the reasons are specific rather than a matter of taste:
707
+
708
+ * **The graph is a path.** States `0..N` with edges `i → i+1` plus resets to `0`. There is nothing to search,
709
+ so `findPath` — the description's real capability — is dead weight.
710
+ * **The self-transition is not declarable.** "Stay in the current state on a mismatch" is the common case, and
711
+ the edge set never contains `i → i`. It works only because the edge-existence assertion in `advance()` is
712
+ commented out (`SimpleStateMachine.js:238`) — a latent trap if those assertions are ever restored.
713
+ * **Initial state is `-1`** (`SimpleStateMachine.js:16`), so every instance needs an explicit `setState(0)`.
714
+ * **It is not the cheap object it looks like.** Each instance carries a description reference and two handler
715
+ arrays (`SimpleStateMachine.js:22-30`) that this design never registers into — while `satisfied` and `window`
716
+ have to live outside it regardless. Three typed-array slots replace all of it with no per-instance object.
717
+
718
+ The requirement was to make good use of existing meep constructs. The honest reading is that this construct
719
+ does not fit this problem: the matcher is a path, not a graph. **`SimpleStateMachineDescription` becomes the
720
+ right tool the moment sequences branch** — alternate motions sharing a prefix, resolved by longest match —
721
+ and that is the point at which to adopt it, not before.
722
+
723
+ ---
724
+
725
+ ## 6. Actions — the bindings/behaviour seam
726
+
727
+ ### 6.1 The seam
728
+
729
+ ```
730
+ DATA (serializable, no code) | CODE (never serialized)
731
+ ----------------------------- | ----------------------
732
+ trigger ----> action name | action name ----> behaviour
733
+ Chord(ctrl, z) "undo" | "undo" ----> editor.actions.undo()
734
+ ```
735
+
736
+ The left side is an `InputMap` component. The right side is an ECS event listener. They meet at a string, and
737
+ nothing else crosses. This is the same seam [`InputSystem`](../systems/InputSystem.js) already uses
738
+ (`InputSystem.js:79`), which is why that component is the one worth keeping the shape of. It is explicitly
739
+ *not* a string-keyed factory registry: no behaviour is constructed from the string and no class is looked up
740
+ by it.
741
+
742
+ ### 6.2 One event, one level, one coordinate
743
+
744
+ ```js
745
+ // discrete — fires on the trigger's rising edge, this instant, no frame delay
746
+ ecd.addEntityEventListener(entity, 'undo', () => editor.actions.undo());
747
+
748
+ // continuous — no listener, no mirrored boolean, no teardown handler
749
+ if (map.isActive('sprint')) { ... }
750
+ map.axis2(v, 'move');
751
+ ```
752
+
753
+ Three accessors for three *questions* — "did it happen", "is it happening", "how much" — not three ways to ask
754
+ one question. The level accessor is what deletes
755
+ [`KeyboardCameraController`](../controllers/KeyboardCameraController.js)'s `controls` struct, the 16 `bind()`
756
+ calls maintaining it, and the `clearControls` handler wired to `on.unlinked` that exists solely to stop the
757
+ camera drifting after teardown. The same boilerplate is duplicated almost verbatim in
758
+ [`prototype_first_person_controller.js`](../../../control/first-person/prototype_first_person_controller.js).
759
+
760
+ Two things `isActive` is not: it is effectively always `false` for a sequence action, whose `test` is a
761
+ single-edge pulse; and it returns `false` for every action on a **suppressed** map (§8).
762
+
763
+ *(One behaviour `KeyboardCameraController` has that the coordinate does not reproduce: it sends a `'user-input'`
764
+ entity event on every pan keydown (`KeyboardCameraController.js:54`), consumed elsewhere as an interaction
765
+ signal. Porting it is a `map.bind('user-input', ...)` alongside the coordinate, and §14 lists it rather than
766
+ leaving it to be discovered missing.)*
767
+
768
+ ### 6.3 The event payload
769
+
770
+ ```js
771
+ export class InputActionEvent {
772
+ action = "";
773
+ /** Driving coordinate, or the pointer position, at the moment the trigger fired. @type {Vector3} */
774
+ value = new Vector3();
775
+ /** The switch whose edge completed the trigger. For a synthetic hold-maturity edge, the first
776
+ * switch in the hold's own source set. @type {InputSource} */
777
+ source = new InputSource();
778
+ /** Arrival timestamp of the completing edge, real time, seconds since ISM start. @type {number} */
779
+ time = 0;
780
+ }
781
+ ```
782
+
783
+ One instance, reused across dispatches, mutated in place. Dispatch is synchronous, so a handler that reads it
784
+ inline is correct and one that retains it is not — stated in the JSDoc, as scratch vectors are elsewhere in
785
+ the engine. A nested edge cannot clobber it mid-read, because nested edges are queued rather than processed
786
+ inline (§2.3).
787
+
788
+ **It carries no DOM information**, and that is a real limit worth naming: consumers that today gate on
789
+ `event.target === graphics.domElement` to tell a canvas click from a click on overlaying UI —
790
+ `CombatInteractionStateMachine` does — have no equivalent here. UI-overlay occlusion is a DOM-tree question
791
+ that §8's map occlusion cannot see. Those consumers are in the gesture set that stays on `InputController`
792
+ (§1.1) for now; a DOM-aware gate is part of the deferred gesture work.
793
+
794
+ ---
795
+
796
+ ## 7. Layers — a stack, addressed by index
797
+
798
+ Layers solve **rebinding**: defaults, then the player's overrides, then a context-specific tweak — one
799
+ conceptual keymap assembled from stacked sources, top wins. They are a stack, addressed by index.
800
+
801
+ ```js
802
+ const map = new InputMap();
803
+
804
+ map.bind('jump', InputTriggerKey.from('space')); // layer 0, the base
805
+ map.bind('undo', InputTriggerChord.from(InputTriggerKey.from('ctrl'),
806
+ InputTriggerKey.from('z')));
807
+
808
+ const user = map.pushLayer(); // index 1
809
+ user.bind('jump', InputTriggerGamepadButton.from(GamepadButtons.a)); // REPLACES space
810
+ ```
811
+
812
+ ```js
813
+ map.layer(i) // InputLayer at index i, asserts existence
814
+ map.pushLayer() // append, return the new top
815
+ map.popLayer() // discard the top
816
+ map.bind(...) // sugar for map.layer(0).bind(...)
817
+ ```
818
+
819
+ Rules, and they are CSS's:
820
+
821
+ * Higher index is higher in the cascade.
822
+ * The cascade is **per action name**, and it applies identically to trigger bindings and coordinate bindings
823
+ (§9.1). The highest layer binding an action supplies *all* of that action's triggers or channels; lower
824
+ layers' entries for it are inert.
825
+ * To keep both, bind both **in one layer** — a binding maps one action to a *list*, exactly as
826
+ `font-family: a, b` lists fallbacks in one declaration:
827
+
828
+ ```js
829
+ map.bind('jump', InputTriggerKey.from('space'), InputTriggerGamepadButton.from(GamepadButtons.a));
830
+ ```
831
+
832
+ Replace-versus-add is a property of *where you write it*, not a flag on `bind`. That is what lets rebinding
833
+ ("the user's jump wins") and multi-device ("either device jumps") both be expressible with no mode switch.
834
+
835
+ * An action named in the top layer that binds it, with nothing bound, is **unbound** — how a keymap layer
836
+ removes an inherited binding:
837
+
838
+ ```js
839
+ map.layer(1).unbind('undo');
840
+ ```
841
+
842
+ * An action name is either a trigger action or a coordinate action, never both — asserted at authoring time,
843
+ because `isActive('move')` and `axis2(v, 'move')` would otherwise disagree about what `'move'` is.
844
+
845
+ Layers carry no names. There is nothing to look up, so nothing to mistype, and serialization is one `uint16`
846
+ count with no strings (§10).
847
+
848
+ ---
849
+
850
+ ## 8. Occlusion — arbitration across entities
851
+
852
+ Layers are within one map. Modality is across maps, and it is a different question: the cascade is *nominal*
853
+ (over action names), occlusion is *physical* (over switches).
854
+
855
+ ```js
856
+ export const InputOcclusion = {
857
+ /** Sees everything, blocks nothing. HUD overlays, debug readouts, recorders. */
858
+ Pass: 0,
859
+ /** Switches consumed by this map's fired triggers are invisible below, for that edge. The default. */
860
+ BlockMatched: 1,
861
+ /** Every map below is suppressed outright. Modal dialogs, pause menus. */
862
+ BlockAll: 2
863
+ };
864
+ ```
865
+
866
+ **The two values do different jobs at different scopes, and revision 2's failure to say so was a real bug.**
867
+
868
+ ### 8.1 `BlockMatched` — dynamic, per edge, dispatch only
869
+
870
+ Per edge, the walk descends maps by `order`. A `consumed : BitSet` over interned switch indices accumulates as
871
+ it goes; a fired trigger adds its own switches, and a trigger whose switches are already consumed does not
872
+ dispatch.
873
+
874
+ **Consumption gates dispatch, not evaluation** — and this is the correction that matters, because the obvious
875
+ alternative is broken. Hiding a consumed edge from a lower map entirely freezes that map's triggers at their
876
+ last observed values, and the next edge they *do* see no longer looks like a change. Concretely: an upper map
877
+ binding `Release(k)` and a lower map binding `k`. Every release is consumed above, so the lower map never
878
+ observes `k` going up; its press trigger fires once and is then dead forever. So lower maps keep evaluating,
879
+ and only their dispatch is suppressed.
880
+
881
+ The honest consequence, since an earlier revision of this document claimed otherwise: a modal-consumed
882
+ keystroke **does** advance a lower map's sequence and **does** run its hold timers. Only the firing is
883
+ withheld. `BlockAll` is what makes a lower map genuinely inert (§8.2), and that is another reason the two
884
+ values are not degrees of the same thing.
885
+
886
+ Consumption is also **unconditional within a map**. Specificity arbitration — `Ctrl+Z` beating a bare `Z`
887
+ (§4.6) — is a property of the map, not of its occlusion, so it holds in a `Pass` map too. What occlusion
888
+ decides is only whether that consumption is still standing when the next map down is walked.
889
+
890
+ `BlockMatched` does not affect polled reads. A pause menu using it stops the combat scene's `Escape` while the
891
+ camera keeps panning behind it — which is exactly what §8 wants of it.
892
+
893
+ ### 8.2 `BlockAll` — static, whole-map, dispatch *and* reads
894
+
895
+ Each linked map carries a computed `suppressed : boolean` — "is there a `BlockAll` map above me". It is
896
+ recomputed when the linked map set, any `order`, or any `occlusion` changes; not per edge.
897
+
898
+ A suppressed map is **inert**: the walk skips it, so nothing dispatches; `isActive` returns `false`; coordinate
899
+ reads return zero; sequence matchers do not advance and hold timers do not run. On un-suppression every
900
+ sequence matcher is re-entered with `enter(0, held, now)` (§5.1), so it resumes against current reality rather
901
+ than stale state.
902
+
903
+ This is the fix for the defect revision 2 shipped. There, occlusion was a per-edge `consumed` set gating
904
+ *firing* only, while `isActive` and the coordinate accessors are **polled** — outside any edge walk, with
905
+ nothing connecting them. So the §11 pause menu stopped edge-driven actions while the §11 camera, which is a
906
+ `Script` polling `map.axis2(pan, 'pan')` over WASD every tick, kept panning: precisely the drifting-world bug
907
+ the modal value exists to prevent. Its two occlusion test cases both passed, because both tested dispatched
908
+ actions only.
909
+
910
+ ### 8.3 Consequences worth stating
911
+
912
+ * **Local co-op needs no fourth value.** `BlockMatched` is per switch, so player 2's pad is untouched by player
913
+ 1's map — they never reference the same switch. `BlockAll` suppresses whole maps and is therefore wrong for
914
+ split-screen; that is a property of the modal hammer, not a hole in it.
915
+ * **`order` ties are resolved by link order**, oldest first. Deterministic, and mentioned because the format
916
+ clamps `order` (§10) and ties are therefore reachable.
917
+ * **Lifetime is entity lifetime.** This is already how the game scopes input:
918
+ `CombatInteractionStateMachine` registers an entity per FSM state and lets `link`/`unlink` turn bindings on
919
+ and off (`CombatInteractionStateMachine.js:113-160`). Put the `InputMap` on the entity owning the context and
920
+ entering or leaving the state *is* the binding lifecycle — no `enable()`/`disable()` bookkeeping, no
921
+ `clearControls` on teardown.
922
+
923
+ ---
924
+
925
+ ## 9. The component and the system
926
+
927
+ ### 9.1 `InputMap`
928
+
929
+ ```js
930
+ export class InputMap {
931
+ static typeName = "InputMap";
932
+ static serializable = true;
933
+
934
+ /** Index 0 is the base. @readonly @type {InputLayer[]} */
935
+ layers = new List();
936
+
937
+ /** Cross-entity arbitration, higher evaluates first. @type {number} */
938
+ order = 0;
939
+
940
+ /** @type {InputOcclusion} */
941
+ occlusion = InputOcclusion.BlockMatched;
942
+
943
+ // ---- authoring -----------------------------------------------------
944
+ bind(action, ...triggers) { } // -> InputMap, writes to layer 0
945
+ bindCoordinate(action, ...channels) { } // -> InputMap, writes to layer 0
946
+ unbind(action) { } // -> InputMap, covers both kinds
947
+ layer(index) { } // -> InputLayer
948
+ pushLayer() { } // -> InputLayer
949
+ popLayer() { }
950
+
951
+ // ---- reading -------------------------------------------------------
952
+ isActive(action) { } // -> boolean
953
+ coordinate(result, offset, action) { } // any dimensionality
954
+ axis1(result, action) { } // Vector1
955
+ axis2(result, action) { } // Vector2
956
+ axis3(result, action) { } // Vector3
957
+
958
+ equals(other) { }
959
+ hash() { }
960
+ }
961
+ ```
962
+
963
+ ```js
964
+ export class InputLayer {
965
+ /** @readonly @type {InputBinding[]} */
966
+ bindings = new List();
967
+ /** @readonly @type {List<InputCoordinateBinding>} */
968
+ coordinates = new List();
969
+
970
+ bind(action, ...triggers) { }
971
+ bindCoordinate(action, ...channels) { }
972
+ unbind(action) { }
973
+ }
974
+
975
+ export class InputBinding {
976
+ action = "";
977
+ /** @readonly @type {InputTrigger[]} */
978
+ triggers = new List();
979
+ }
980
+
981
+ export class InputCoordinateBinding {
982
+ action = "";
983
+ /** @readonly @type {InputCoordinateChannel[]} */
984
+ channels = new List();
985
+ }
986
+ ```
987
+
988
+ **Coordinate bindings are a first-class list on the layer, not triggers.** Revision 2 exposed
989
+ `bindCoordinate()` with nowhere to store the result — `InputBinding` holds triggers, and a channel is not a
990
+ trigger — so coordinate bindings existed in the API, in the examples and in the argument for deleting
991
+ `KeyboardCameraController`, but in neither the component nor the wire format.
992
+
993
+ **Multiple channels on one action resolve by priority: the first channel with any non-zero component wins.**
994
+ Deterministic, allocation-free, and the behaviour consumers already hand-write — `GamepadDevice`'s own
995
+ documented example is "stick wins, else D-pad". It also avoids the alternative's arithmetic nonsense, where
996
+ keyboard `D` plus a stick at 0.3 would sum to 1.3. All channels bound to one action must agree on
997
+ `dimensions`; asserted at authoring time.
998
+
999
+ The simple case never mentions layers, occlusion or order:
1000
+
1001
+ ```js
1002
+ const map = new InputMap();
1003
+ map.bind('jump', InputTriggerKey.from('space'));
1004
+ ```
1005
+
1006
+ ### 9.2 `InputMapSystem`
1007
+
1008
+ ```js
1009
+ export class InputMapSystem extends System {
1010
+ dependencies = [InputMap];
1011
+
1012
+ registerDevice(adapter) { } // may be called after maps are linked; recompiles them (§3.1)
1013
+
1014
+ link(map, entity) { } // compile (§9.3), subscribe to newly-referenced switches
1015
+ unlink(map, entity) { } // release; unsubscribe switches no live map references
1016
+ update(dt) { } // time only: hold maturity, sequence windows, Delta reset, suppression
1017
+
1018
+ setRecorder(recorder) { } // §2.5 — DESIGNED, NOT BUILT
1019
+ }
1020
+ ```
1021
+
1022
+ Construction takes adapters rather than `engine.devices`, so the system has no dependency on the shape of that
1023
+ object and is constructible in a test with nothing behind it:
1024
+
1025
+ ```js
1026
+ new InputMapSystem([
1027
+ new KeyboardInputDeviceAdapter(devices.keyboard),
1028
+ new PointerInputDeviceAdapter(devices.pointer),
1029
+ new GamepadInputDeviceAdapter(devices.gamepad)
1030
+ ]);
1031
+ ```
1032
+
1033
+ Each interned switch is subscribed once, with a per-switch `SourceBinding {index, system}` passed as the
1034
+ `Signal` handler's context (`Signal.js:151`), so the edge path allocates nothing and needs no closure per
1035
+ event.
1036
+
1037
+ *(As built, the system declares no `components_used`, and does not need to: relative channels LATCH rather
1038
+ than clear (§3.3), so every reader in a frame sees the same value regardless of where the system runs in it.
1039
+ An earlier revision of this section leaned on execution order instead, which was the weaker guarantee.)*
1040
+
1041
+ **Unlinking a map with a sequence mid-match** discards its matcher state along with the compiled program;
1042
+ there is nothing to leak and nothing to resume. Unlinking with switches held is likewise clean: `#held` is
1043
+ owned by the system, not the map, and the map's triggers are simply gone.
1044
+
1045
+ ### 9.3 Compilation: the stylesheet / computed-style split
1046
+
1047
+ The `InputMap` component is *authored* data — layered, editable. It is not what the edge path reads. On `link`,
1048
+ on `registerDevice`, and on any structural change, the system compiles it into an internal `InputMapProgram`:
1049
+
1050
+ * cascade resolved — one flat action table per kind, layers already collapsed;
1051
+ * switches interned to `Uint32` indices against a system-wide table;
1052
+ * per switch, the list of triggers it can affect (`collectSources`), pre-sorted into §4.6 specificity order, so
1053
+ an edge touches only triggers that could possibly care;
1054
+ * every trigger's `satisfied` initialised from current state, not from `false` (§4.5);
1055
+ * sequence matcher state allocated as three typed-array slots per sequence binding (§5.1).
1056
+
1057
+ The edge path then touches integers, typed arrays and `BitSet`s only. No string comparison, no `List`
1058
+ traversal, no allocation per event. Recompiles requested during dispatch are queued and applied at the end of
1059
+ the current edge (§2.3), so a walk always completes against the program it started with.
1060
+
1061
+ ---
1062
+
1063
+ ## 10. Serialization
1064
+
1065
+ ```js
1066
+ export class InputMapSerializationAdapter extends BinaryClassSerializationAdapter {
1067
+ klass = InputMap;
1068
+ version = 0;
1069
+
1070
+ serialize(buffer, map) { }
1071
+ deserialize(buffer, map) { }
1072
+ }
1073
+ ```
1074
+
1075
+ ```
1076
+ InputMap
1077
+ int16 order see below
1078
+ uint8 occlusion InputOcclusion
1079
+ uint8 device_count
1080
+ repeat device_count x utf8 device table - every InputDeviceAdapter#id referenced
1081
+ uint16 layer_count
1082
+ repeat layer_count x InputLayer index 0 first; layers carry no name
1083
+
1084
+ InputLayer
1085
+ uint16 binding_count
1086
+ repeat binding_count x InputBinding
1087
+ uint16 coordinate_binding_count
1088
+ repeat coordinate_binding_count x InputCoordinateBinding
1089
+
1090
+ InputBinding
1091
+ utf8 action
1092
+ uint8 trigger_count
1093
+ repeat trigger_count x InputTrigger
1094
+
1095
+ InputCoordinateBinding
1096
+ utf8 action
1097
+ uint8 channel_count
1098
+ repeat channel_count x InputCoordinateChannel
1099
+
1100
+ InputTrigger
1101
+ uint8 type InputTriggerType
1102
+ Switch : InputSource
1103
+ Direction : InputCoordinateChannel, uint8 direction, float32 threshold, float32 hysteresis
1104
+ Chord : uint8 child_count, children
1105
+ Sequence : uint8 child_count, children, float32 step_timeout
1106
+ Hold : child, float32 duration
1107
+ Release : child
1108
+
1109
+ InputCoordinateChannel
1110
+ uint8 type InputCoordinateChannelType
1111
+ Device : InputCoordinateSource
1112
+ Switches : uint8 count, count x InputTrigger (2 per dimension: negative, positive)
1113
+ Compose : uint8 count, count x InputCoordinateChannel
1114
+
1115
+ InputSource
1116
+ uint8 device_index into the map's device table
1117
+ uint16 code
1118
+
1119
+ InputCoordinateSource
1120
+ uint8 device_index
1121
+ uint16 code
1122
+ uint8 dimensions
1123
+ uint8 kind InputCoordinateKind
1124
+ ```
1125
+
1126
+ Decisions, not incidentals:
1127
+
1128
+ * **`order` is `int16`.** Revision 2 wrote it as `uint8 + 128`, range `[-128, 127]` — and its own modal example
1129
+ used `order = 1000`, which clamped to 127 on load and could silently tie with any other high map. `int16`
1130
+ covers the examples and leaves headroom; ties resolve by link order (§8.3).
1131
+ * **Coordinate bindings are reachable from the root.** Revision 2 defined an `InputCoordinateChannel` encoding
1132
+ that nothing referenced, so no map containing a coordinate could round-trip.
1133
+ * **`Direction` serializes a channel, not a source.** It accepts any `InputCoordinate*` channel (§4.2), so
1134
+ writing only an `InputCoordinateSource` made direction-over-composed-channel constructible and
1135
+ unserializable.
1136
+ * **A device table, not a string per source.** A 40-binding keyboard map writes `"keyboard"` once rather than
1137
+ forty times.
1138
+ * **Every string here is an identifier.** `writeUTF8String` is ASCII-only in practice and `readUTF8String` is
1139
+ NUL-terminated by design, so this must be stated: action names and device ids are ASCII identifiers, not
1140
+ localized text. A binding UI shows a label looked up *from* the action name; it never serializes the label.
1141
+ * **Codes come from enums, three of which are new.** `KeyCodes`, `GamepadButtons` and `GamepadAxes` are already
1142
+ stable on-disk quantities; `MouseButtons`, `PointerCoordinates` and `GamepadCoordinates` are introduced here
1143
+ and are append-only once shipped (§3.1). Revision 2 claimed "no new numbering to keep in sync", which was
1144
+ false the moment it introduced them.
1145
+ * **Triggers and channels nest, so serialization recurses.** One `uint8` discriminator each, dispatched by a
1146
+ `switch` — verbose, typed and greppable, over a string-keyed constructor table.
1147
+
1148
+ ---
1149
+
1150
+ ## 11. End to end
1151
+
1152
+ **Gameplay — camera, mixed devices:**
1153
+
1154
+ ```js
1155
+ const map = new InputMap();
1156
+
1157
+ map.bindCoordinate('pan',
1158
+ InputCoordinateGamepad.from(GamepadCoordinates.left_stick), // first non-zero wins
1159
+ InputCoordinateSwitches.from([
1160
+ InputTriggerKey.from('a'), InputTriggerKey.from('d'),
1161
+ InputTriggerKey.from('w'), InputTriggerKey.from('s')
1162
+ ]));
1163
+ map.bind('user-input', InputTriggerKey.from('a'), InputTriggerKey.from('d'),
1164
+ InputTriggerKey.from('w'), InputTriggerKey.from('s'));
1165
+ map.bind('focus_selection', InputTriggerKey.from('f'));
1166
+
1167
+ new Entity()
1168
+ .add(map)
1169
+ .add(new Script(dt => {
1170
+ map.axis2(pan, 'pan');
1171
+ apply_radial_dead_zone(pan, pan.x, pan.y, 0.15, 0.95); // policy, at the call site
1172
+ controller.pan(pan, dt);
1173
+ }))
1174
+ .build(ecd);
1175
+ ```
1176
+
1177
+ Both channels feed one action in one call, so the cascade replaces them as a unit; the stick wins while
1178
+ deflected and the keys take over when it rests. The `'user-input'` binding is
1179
+ `KeyboardCameraController.js:54`'s interaction signal, carried over explicitly rather than lost.
1180
+
1181
+ **Editor — `EditorKeyMap` as data:**
1182
+
1183
+ ```js
1184
+ const ctrl = InputTriggerKey.from('ctrl');
1185
+
1186
+ const map = new InputMap();
1187
+ map.order = 100;
1188
+
1189
+ map.bind('undo', InputTriggerChord.from(ctrl, InputTriggerKey.from('z')));
1190
+ map.bind('redo', InputTriggerChord.from(ctrl, InputTriggerKey.from('y')));
1191
+ map.bind('copy', InputTriggerChord.from(ctrl, InputTriggerKey.from('c')));
1192
+ map.bind('paste', InputTriggerChord.from(ctrl, InputTriggerKey.from('v')));
1193
+ map.bind('save', InputTriggerChord.from(ctrl, InputTriggerKey.from('s')));
1194
+ map.bind('duplicate', InputTriggerChord.from(InputTriggerKey.from('shift'), InputTriggerKey.from('d')));
1195
+ map.bind('delete', InputTriggerKey.from('delete'), InputTriggerKey.from('x'));
1196
+
1197
+ ecd.addEntityEventListener(entity, 'undo', () => editor.actions.undo());
1198
+ ```
1199
+
1200
+ `Ctrl+Z` beats the bare `Z` binding under §4.6 (modifier-first; see §4.6's stated condition), and the
1201
+ `if (event.ctrlKey)` split disappears. The bindings are serializable, so the editor gets a rebinding panel for
1202
+ free — the thing that could not exist while they were `if` statements. §14.1 lists the three `EditorKeyMap`
1203
+ behaviours that do *not* come across.
1204
+
1205
+ **Modal — pause menu:**
1206
+
1207
+ ```js
1208
+ const map = new InputMap();
1209
+ map.order = 1000;
1210
+ map.occlusion = InputOcclusion.BlockAll;
1211
+ map.bind('resume', InputTriggerKey.from('escape'));
1212
+ ```
1213
+
1214
+ Every map below is suppressed: no dispatch, and `isActive`/`axis2` read neutral — so the camera above stops
1215
+ rather than drifting behind the dialog (§8.2).
1216
+
1217
+ **Cross-device chords:**
1218
+
1219
+ ```js
1220
+ map.bind('precision_place', InputTriggerChord.from(InputTriggerKey.from('ctrl'),
1221
+ InputTriggerMouseButton.from(MouseButtons.left)));
1222
+ map.bind('pad_reset', InputTriggerChord.from(InputTriggerGamepadButton.from(GamepadButtons.back),
1223
+ InputTriggerKey.from('r')));
1224
+ ```
1225
+
1226
+ ---
1227
+
1228
+ ## 12. Testing
1229
+
1230
+ ### 12.1 No mocks, and no DOM
1231
+
1232
+ The seam is `InputDeviceSwitch`: a real object with `press()`/`release()`, constructible in node with nothing
1233
+ beneath it. A test adapter is a *real* adapter over *real* switches — not a mock. Eager evaluation keeps the
1234
+ tests short, because there is no tick to pump for edge-driven behaviour:
1235
+
1236
+ ```js
1237
+ const kb = new TestInputDeviceAdapter('keyboard', KeyCodes);
1238
+ const system = new InputMapSystem([kb]);
1239
+ // real EntityComponentDataset, real InputMap, real listeners
1240
+
1241
+ kb.switch(KeyCodes.ctrl).press();
1242
+ kb.switch(KeyCodes.z).press();
1243
+
1244
+ expect(observed).toEqual(['undo']); // no system.update() anywhere
1245
+ ```
1246
+
1247
+ `system.update(dt)` appears only in tests for hold maturity, sequence windows, `Delta` reset and suppression —
1248
+ a precise statement of what the tick is for.
1249
+
1250
+ *(Revision 2 justified this by claiming `InputSystem` "cannot be exercised without a focusable DOM element".
1251
+ That was overstated: focusability is `KeyboardDevice`'s requirement (`KeyboardDevice.js:61-66`), and
1252
+ `InputSystem` resolves paths against any object, so a synthetic tree of real Signals would exercise it. The
1253
+ real point is narrower and still worth making — nobody has, and `InputBinding.spec.js` asserts only that a
1254
+ constructor does not throw.)*
1255
+
1256
+ ### 12.2 What gets asserted
1257
+
1258
+ Observable behaviour only: dispatched action names and order, `isActive` levels, coordinate values. Never which
1259
+ triggers were evaluated, never how many times a matcher ran, never internal matcher state.
1260
+
1261
+ | | Case | Guards |
1262
+ |---|---|---|
1263
+ | 1 | An action fires before any `update()` runs | §2 eager evaluation — the requirement, as a test |
1264
+ | 2 | Press and release inside one frame both fire; two presses dispatch in press order | §2 |
1265
+ | 3 | `Ctrl` then `Z` fires only `undo`; `Z` then `Ctrl` fires both, in that order | §4.6 and its stated condition |
1266
+ | 4 | One long press of `A` does not satisfy both steps of `[A, A]` | §5.1 entry snapshot |
1267
+ | 5 | `[A,A,B]` fires on `A A A B` | §5.3 |
1268
+ | 6 | Holding `A` and tapping `B` twice fires `[A,B]` **once** | §5.3 post-fire re-entry — the revision-2 machine-gun |
1269
+ | 7 | A window expiry with the first step's key held does not spuriously advance | §5.1 — the revision-2 stale-snapshot bug |
1270
+ | 8 | `Ctrl+Shift+A > B` fires whether or not modifiers are released first | §5.4 |
1271
+ | 9 | `↓ ↘ →` over a stick coordinate fires; the same over raw d-pad *buttons* does not | §5.4 — the exclusivity claim, both directions |
1272
+ | 10 | Charge (`hold ← 2 s > →`) fires after a 10 s hold; returning to neutral instead expires it | §5.2 window refresh — the revision-2 charge failure |
1273
+ | 11 | A sequence window expires only after the previous step stops holding | §5.2 |
1274
+ | 12 | `Release(Chord(a,b))` does not fire on the first edge touching `a` | §4.5 `satisfied` initialisation |
1275
+ | 13 | A handler that removes its own entity, or rebinds its own map, does not corrupt the walk | §2.3 |
1276
+ | 14 | A `BlockAll` map zeroes a lower map's `axis2` and `isActive`, not just its dispatch | §8.2 — the revision-2 drifting camera |
1277
+ | 15 | A `BlockMatched` map does **not** stop a lower map's unrelated action or its coordinate reads | §8.1 — the converse |
1278
+ | 16 | A lower map's press binding still fires after many edges consumed above it | §8.1 — the stale-edge trap |
1279
+ | 17 | Un-suppressing a map re-enters its sequences at 0 rather than resuming stale state | §8.2 |
1280
+ | 18 | The top layer's `jump` replaces the base layer's `jump`; two triggers in one layer both fire | §7 |
1281
+ | 19 | A coordinate action bound to stick + keys reads the stick while deflected, the keys at rest | §9.1 priority |
1282
+ | 20 | A gamepad binding with no pad connected links and never fires; so does an unregistered device id | §3.1 totality |
1283
+ | 21 | `registerDevice` after link makes previously inert bindings live | §3.1, §9.3 |
1284
+ | 22 | Two wheel events in one frame sum for tick-time readers | §3.3 `Delta` |
1285
+ | 23 | A 3-D wheel coordinate round-trips all three components | §3.3 dimensionality |
1286
+ | 24 | Serialize, deserialize, replay a recorded trace, get the same dispatches | §10, §2.5 |
1287
+
1288
+ Cases 6, 7, 10 and 14 are regressions against defects revision 2 actually contained; write them first. Case 9
1289
+ is the only claim in §5.4 that could be wrong in a way the others would not catch.
1290
+
1291
+ ---
1292
+
1293
+ ## 13. The trigger DSL — planned, deferred, optional
1294
+
1295
+ **Not implemented in this design.** Specified here so the trigger classes are shaped to accept it, and so it
1296
+ is not invented ad hoc later.
1297
+
1298
+ ### 13.1 What it is
1299
+
1300
+ A text syntax compiling to the same `InputTrigger` objects §4 defines. It is **an additional entry point,
1301
+ never a required one**: an author can compose triggers by hand or write them as text, and the runtime cannot
1302
+ tell the results apart.
1303
+
1304
+ ```js
1305
+ import { InputTriggerDSL } from "./dsl/InputTriggerDSL.js";
1306
+
1307
+ map.bind('promote', InputTriggerDSL.compile("keyboard:ctrl + keyboard:shift + keyboard:a > keyboard:b"));
1308
+ ```
1309
+
1310
+ ### 13.2 Grammar
1311
+
1312
+ ```ebnf
1313
+ trigger = sequence ;
1314
+ sequence = chord , { ">" , chord } , [ "within" , NUMBER ] ;
1315
+ chord = unary , { "+" , unary } ;
1316
+ unary = [ "!" ] , primary ;
1317
+ primary = atom | "(" , trigger , ")" | call ;
1318
+ call = IDENT , "(" , [ trigger , { "," , trigger | NUMBER } ] , ")" ;
1319
+ atom = DEVICE , ":" , BINDPOINT , [ "." , SUBPOINT ] ;
1320
+ DEVICE = IDENT ;
1321
+ BINDPOINT = IDENT | NUMBER ;
1322
+ SUBPOINT = IDENT ;
1323
+ ```
1324
+
1325
+ | Form | Means | Compiles to |
1326
+ |---|---|---|
1327
+ | `keyboard:space` | a key | `InputTriggerKey` |
1328
+ | `pointer:left` | a mouse button | `InputTriggerMouseButton` |
1329
+ | `gamepad:a` | a pad button | `InputTriggerGamepadButton` |
1330
+ | `gamepad:left_stick.down` | a direction on a coordinate | `InputTriggerDirection` |
1331
+ | `a + b` | chord | `InputTriggerChord` |
1332
+ | `a > b` | sequence | `InputTriggerSequence` |
1333
+ | `!a` | release / negation | `InputTriggerRelease` |
1334
+ | `hold(a, 0.5)` | held for 0.5 s | `InputTriggerHold` |
1335
+ | `(...)` | grouping | — |
1336
+ | `... within 0.2` | step timeout | `withStepTimeout` |
1337
+
1338
+ **Precedence, tightest first:** `!` → `+` → `>` → `within`, so
1339
+ `keyboard:ctrl + keyboard:shift + keyboard:a > keyboard:b` parses as intended with no parentheses.
1340
+
1341
+ **Keyword collision** is resolved by the `:` — an identifier followed by `:` is a device name, so a device may
1342
+ be called `hold` without ambiguity. Worth stating because it is where small DSLs usually break.
1343
+
1344
+ The §5.4 motion, written out:
1345
+
1346
+ ```
1347
+ gamepad:left_stick.down > gamepad:left_stick.down_right > gamepad:left_stick.right + gamepad:x within 0.2
1348
+ ```
1349
+
1350
+ ### 13.3 Why it stays separate
1351
+
1352
+ * **`ism/dsl/` is its own module**, imported by nothing in the runtime. A game that composes triggers by hand
1353
+ never pays for a parser — it is not in the bundle.
1354
+ * **It is a compiler, not a representation.** Serialization stays binary (§10), so a DSL change can never
1355
+ invalidate saved bindings.
1356
+ * **`InputTriggerDSL.stringify(trigger)` is the inverse**, which is what a rebinding UI wants: it shows a
1357
+ binding in the syntax an author would type, with no second formatting path.
1358
+
1359
+ ### 13.4 Deferred until
1360
+
1361
+ Everything in §14 phases 1–8 has landed and at least one consumer has migrated. The DSL is a convenience over a
1362
+ working system; building it first would freeze the trigger vocabulary before the vocabulary has been used.
1363
+
1364
+ ---
1365
+
1366
+ ## 14. Migration
1367
+
1368
+ The ISM lands beside the existing systems. Both keep working; neither is touched until its consumers move.
1369
+ **`InputController` and `InputControllerSystem` survive this program** — they still own every gesture binding
1370
+ (§1.1).
1371
+
1372
+ *This table is the plan of work, not a progress report. For where each phase actually stands, read the status
1373
+ section of [`ISM_IMPLEMENTATION_PLAN.md`](ISM_IMPLEMENTATION_PLAN.md), which is kept current as phases land.*
1374
+
1375
+ | Phase | Work | Gate |
1376
+ |---|---|---|
1377
+ | **1** | `InputSource`, `InputCoordinateSource`, the `InputTrigger*` classes, `InputMap` / `InputLayer` / `InputBinding` / `InputCoordinateBinding` | Value-object specs: `equals` / `hash` / round-trip |
1378
+ | **2** | `InputDeviceAdapter` and the three adapters; the `MouseButtons`, `PointerCoordinates`, `GamepadCoordinates` enums | Totality: every declared code resolves with no hardware, and with no adapter (§12.2 cases 20–21) |
1379
+ | **3** | `InputMapSystem`: eager edge path, `#held`, compile, switch/chord/release, cascade, `BlockMatched`, ECS dispatch, `isActive` | cases 1–3, 12, 13, 15, 18 |
1380
+ | **4** | `InputTriggerSequence` + `InputTriggerHold`; synthetic maturity edges; window refresh and expiry | cases 4–8, 10, 11, 16 |
1381
+ | **5** | Coordinates: channels, priority resolution, the `Delta` latch, read accessors | cases 19, 22, 23; retires `KeyboardCameraController` |
1382
+ | **6** | `InputTriggerDirection` over coordinates, with threshold and hysteresis | case 9 |
1383
+ | **7** | `BlockAll` suppression across dispatch and reads; un-suppression re-entry | cases 14, 17 |
1384
+ | **8** | `InputMapSerializationAdapter` + `InputRecorder` | case 24 |
1385
+ | **9** | Migrate consumers (below) | existing specs stay green |
1386
+ | **10** | `ism/dsl/` — the §13 compiler and `stringify` | round-trip: `compile(stringify(t))` equals `t` |
1387
+
1388
+ ### 14.1 Consumers
1389
+
1390
+ 1. **`EditorKeyMap`** — **blocked, not retired.** The engine's `KeyboardDevice` is mounted on
1391
+ `viewStack.el` (`Engine.js:367`) and `Editor.js:563` pushes the entire editor GUI into that same stack, so
1392
+ an editor map would see keys typed into panel text fields — `x` would delete the selected entities, and
1393
+ `preventDefault` would swallow the keystroke. The ISM has no DOM awareness to express the old gate (§6.3),
1394
+ so retiring this needs a design addition. See the implementation plan §5.1. Four behaviours would have to be
1395
+ re-provided in any case, because none of them is in the ISM:
1396
+ * **Focus gating.** `isViewFocused` (`EditorKeyMap.js:12-31`) suppresses all editor bindings when focus is
1397
+ outside the game view. In the ISM this depends on which element the editor's `KeyboardDevice` is mounted
1398
+ on. With bare-key bindings like `x` = delete, getting it wrong is destructive — verify before landing.
1399
+ * **Autorepeat.** Holding `Ctrl+Z` currently repeat-undoes; it will not (§5.5). Decide per action whether
1400
+ that is a loss or a fix.
1401
+ * **`stopPropagation()`** on handled chords — currently prevents the event reaching other listeners.
1402
+ * **The unconditional forward to the active tool** (`EditorKeyMap.js:175-180`), which `Tool.handleKeyboardEvent`
1403
+ relies on: each tool gets its own `InputMap` on its activation entity, at a higher `order`.
1404
+ 2. **`KeyboardCameraController`** — retired by §3.3, not ported. Carry the `'user-input'` event (§11).
1405
+ 3. **`Input` / `InputSystem`** — **done.** All five consumers moved and both are `@deprecated`.
1406
+ `OverlayPageGUI` (escape), `CombatInteractionController` (p, escape) and two harnesses were straight
1407
+ translations. `StoryManager` was a split: it bound both a tap and the space bar to one action, and only
1408
+ the space bar moved. A tap is not a button press (§1.1) — translating it would change advance-on-release
1409
+ to advance-on-press and fire at the start of every drag — so the tap kept a small `InputController`, which
1410
+ is the decision the earlier revision of this section left open.
1411
+ 4. **`InputController` keyboard bindings** — **not started.** The keyboard subset moves; `priority` becomes
1412
+ `order`, `exclusive` becomes `InputOcclusion.BlockMatched`. Gesture bindings stay put, which for
1413
+ `StrategyInteractionController` means most of the file. This is where escape arbitration finally bites
1414
+ (plan §5.4) and where `KeyboardCameraController`'s `'user-input'` entity subtlety has to be handled.
1415
+ 5. **`components/InputBinding.js`** (`{path, listener, …}`) is orphaned only when `InputController` finally
1416
+ goes, which is not in this program.
1417
+
1418
+ ### 14.2 A naming collision to plan around
1419
+
1420
+ *(Resolved: the new record lives at `ism/map/InputBinding.js`, so the two never shared a filename and the old
1421
+ one kept its path and its importer. What follows is the problem as it stood.)*
1422
+
1423
+ Phase 1 introduces `InputBinding` in `ism/`, and `ism/InputBinding.js` already exists as the old
1424
+ `{path, event}` record imported by [`Input.js:3`](../components/Input.js). The old file must survive until
1425
+ `Input` is retired (phase 9), so the two cannot share a filename. Land the new one as `ism/InputBinding.js`
1426
+ only after the old is deleted; until then it lives beside its siblings under a transitional name, or `Input`
1427
+ is repointed at a copy under `components/`.
1428
+
1429
+ ---
1430
+
1431
+ ## 15. Explicitly not doing
1432
+
1433
+ * **Pointer gestures** — tap, drag, move, wheel-as-gesture, pinch. §1.1, with the binding counts. Revision 2
1434
+ claimed "nothing in the codebase binds pinch today"; that was false —
1435
+ `StrategyInteractionController.js:437,447` binds `pinchStart` and `pinch`, and it is also the largest
1436
+ migration target.
1437
+ * **Buffering input to a frame boundary.** §2. Revision 1 did, and it bought nothing eager evaluation does not
1438
+ also provide.
1439
+ * **Discriminating tap from hold on one switch.** §5.5 — it requires withholding the short action, which is the
1440
+ buffering above. Accepted as a cost.
1441
+ * **Key autorepeat.** §5.5 — not observable through `InputDeviceSwitch` at all.
1442
+ * **Keeping string paths.** `resolvePath` throws where it should resolve and cannot express two sources at once;
1443
+ `GAMEPAD_API_PROPOSAL.md §5` says not to grow it.
1444
+ * **A trigger class per gesture pattern.** Double-tap is a two-step sequence with a short window; long-press is
1445
+ `InputTriggerHold`.
1446
+ * **Dead zones, sensitivity or response curves on analog output.** §3.3. The direction threshold and hysteresis
1447
+ in §3.4 are not exceptions: producing a boolean from a coordinate cannot be done without them.
1448
+ * **A `Started` / `Performed` / `Canceled` phase enum, or an `edge` field.** §4.1 — every trigger is a
1449
+ predicate and every binding fires on its rising edge.
1450
+ * **A per-binding `consume` flag.** Occlusion is a property of a *context*, not of one binding, and a boolean
1451
+ per binding makes behaviour depend on declaration order.
1452
+ * **Named layers.** §7 — a stack is addressed by index.
1453
+ * **Behaviour in the component.** No function references, no script names, no serialized handler ids (§6.1).
1454
+ * **DOM-target awareness in `InputActionEvent`.** §6.3 — it belongs with the deferred gesture work.
1455
+ * **Strict sequence rejection**, and **button buffering**. §5.5, both deferred with known shapes.
1456
+ * **A generic input node graph.** The trigger classes are already a small algebra; the DSL (§13) is the cheaper
1457
+ first step over it.
1458
+
1459
+ ---
1460
+
1461
+ ## 16. Resolved decisions
1462
+
1463
+ 1. **Analog channels are N-dimensional "coordinates", not axes** — §3.3. `WheelEvent` has three components and
1464
+ `PointerDevice` surfaces all three. `kind` (`Absolute` / `Delta` / `Normalized`) carries the verb.
1465
+ 2. **`order` stays author-set** — §8. A GUI-hierarchy-derived order would need no hand-picked numbers but would
1466
+ couple input arbitration to that hierarchy.
1467
+ 3. **No sensitivity in bindings** — §3.3. A rebinding UI serializes *which* channel drives an action, never how
1468
+ twitchy it is.
1469
+ 4. **Layers are an index-addressed stack, with no names** — §7.
1470
+ 5. **Evaluation is eager, on the edge** — §2. No buffer, no added frame. The costs — re-entrant dispatch,
1471
+ arrival-ordering, the §4.6 chord condition, the §5.5 tap-versus-hold limit, and the §3.3 `Delta` split
1472
+ between eager and tick-time readers — are stated where they arise rather than hidden.
1473
+ 6. **The trigger DSL is planned, specified and deferred** — §13, phase 10.
1474
+ 7. **Pointer gestures are out of scope** — §1.1. `InputController` is reduced, not retired.
1475
+ 8. **`SimpleStateMachine` is not used** — §5.6. The matcher is a path, not a graph; the class becomes the right
1476
+ tool if sequences ever branch.
1477
+
1478
+ ---
1479
+
1480
+ ## 17. What changed in revision 3, and why
1481
+
1482
+ An adversarial review of revision 2 found three defects that made it unimplementable, plus a set of false
1483
+ claims about the existing codebase. Everything below was verified against source before being changed.
1484
+
1485
+ ### Defects
1486
+
1487
+ | | Was | Now |
1488
+ |---|---|---|
1489
+ | **Sequence matcher** | Reset paths (window expiry, post-fire) did not re-establish the entry snapshot, and the advance step indexed `steps[N]`. Holding `A` and mashing `B` machine-gunned `[A,B]` off one `A` press; an expiry with the first key held spuriously advanced with no edge. | Every entry routes through `enter(k, held, now)` (§5.1), which snapshots and never indexes `steps[N]`. Cases 6 and 7. |
1490
+ | **Occlusion** | A per-edge `consumed` set gating *firing* only. `isActive` and coordinate reads are polled, so `BlockAll` could not stop them — a pause menu left the camera panning, the exact bug §8 claimed to prevent. Both occlusion tests passed anyway, testing only dispatch. | Two scopes: `BlockMatched` stays dynamic and dispatch-only; `BlockAll` sets a static per-map `suppressed` covering dispatch *and* reads (§8.2). Cases 14, 15, 17. |
1491
+ | **Coordinate bindings** | `bindCoordinate()` existed with nowhere to store its result, and §10's channel encoding was unreachable from the format root. No map with a coordinate could serialize. `bindCoordinate` was also called twice for one action in §11 with no defined meaning. | `InputCoordinateBinding` on the layer, reachable in the format, with priority resolution (§9.1). Case 19. |
1492
+
1493
+ ### Corrections
1494
+
1495
+ * **Charge inputs failed.** The step window ran from the advance, so any charge longer than
1496
+ `duration + step_timeout` expired before the flick. It now measures time since the previous step *stopped*
1497
+ holding (§5.2). Case 10.
1498
+ * **`satisfied` initialisation was unspecified**, and zero-init makes `Release(Chord(a,b))` fire before the
1499
+ chord exists. Now snapshotted at compile (§4.5). Case 12.
1500
+ * **`order` was `uint8`-biased**, range `[-128,127]`, while §11's own modal used `1000`. Now `int16` (§10).
1501
+ * **`MouseButtons` cited the wrong standard.** The `MouseEvent.buttons` *bitmask* order is left/right/middle;
1502
+ `PointerDevice.buttons[]` is indexed by `MouseEvent.button` *ordinal*. Following the citation would have
1503
+ fired every right-click binding on the middle button, into saved data (§3.1).
1504
+ * **`Release(sequence)` was claimed to mean "stopped matching".** It cannot — a sequence's `test` is a pulse
1505
+ (§4.3).
1506
+ * **Specificity did not rank `Hold` / `Release` / `Direction`**, so they fell through to declaration order —
1507
+ the accident §4.6 claims to eliminate (§4.6).
1508
+ * **Chord-vs-subset arbitration was stated unconditionally.** It holds only when the modifier arrives first
1509
+ (§4.6). Case 3 now tests both orders.
1510
+ * **The `Delta` "same value for every reader" invariant** was a leftover from the buffered design and is false
1511
+ under eager dispatch (§3.3).
1512
+ * **Re-entrancy covered only structural change.** Nested edges and mid-dispatch binding edits are now specified
1513
+ (§2.3).
1514
+ * **The recorder captured only switch edges**, so no map with a coordinate or a hold could replay. It now
1515
+ records coordinate samples and per-frame `dt` (§2.5).
1516
+ * **Unregistered devices had no defined behaviour** (§3.1).
1517
+ * **`EditorKeyMap` and `KeyboardCameraController` deletions dropped real behaviour** — focus gating,
1518
+ autorepeat, `stopPropagation`, the tool forward, the `'user-input'` event — now inventoried (§14.1).
1519
+ * **Counts and claims:** `KeyboardCameraController` has 16 `bind()` calls, not eight; the `InputController`
1520
+ path-string count flattered a migration that is mostly gestures (§1.1); `InputControllerSystem`'s `deferred`
1521
+ array is dead code, not a working mitigation (§2.3); "`InputSystem` cannot be tested without a DOM element"
1522
+ was overstated (§12.1); "no new numbering to keep in sync" was false (§10); `PointerDevice`'s wheel reports
1523
+ `sign()`, so a `Delta` wheel channel counts ticks rather than distance (§3.3).