@woosh/meep-engine 2.154.0 → 2.156.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 (487) hide show
  1. package/README.md +1 -1
  2. package/build/bundle-worker-image-decoder.js +1 -1
  3. package/build/bundle-worker-terrain.js +1 -1
  4. package/editor/view/ecs/ComponentControlView.d.ts +0 -9
  5. package/editor/view/ecs/ComponentControlView.js +2 -98
  6. package/package.json +1 -1
  7. package/src/core/binary/32BitEncoder.js +1 -1
  8. package/src/core/binary/to_half_float_uint16.js +3 -3
  9. package/src/core/bvh2/bvh3/ebvh_build_hierarchy_radix.d.ts.map +1 -1
  10. package/src/core/bvh2/bvh3/ebvh_build_hierarchy_radix.js +275 -253
  11. package/src/core/cache/Cache.d.ts.map +1 -1
  12. package/src/core/cache/Cache.js +7 -0
  13. package/src/core/cache/FrequencySketch.d.ts.map +1 -1
  14. package/src/core/cache/FrequencySketch.js +8 -4
  15. package/src/core/clipboard/obtainClipBoard.d.ts +6 -0
  16. package/src/core/clipboard/obtainClipBoard.d.ts.map +1 -0
  17. package/src/core/clipboard/obtainClipBoard.js +29 -0
  18. package/src/core/clipboard/safeClipboardReadText.d.ts +6 -0
  19. package/src/core/clipboard/safeClipboardReadText.d.ts.map +1 -0
  20. package/src/core/clipboard/safeClipboardReadText.js +55 -0
  21. package/src/core/clipboard/safeClipboardWriteText.d.ts +8 -0
  22. package/src/core/clipboard/safeClipboardWriteText.d.ts.map +1 -0
  23. package/src/core/clipboard/safeClipboardWriteText.js +23 -0
  24. package/src/core/collection/array/array_quick_sort_by_lookup_map.js +1 -1
  25. package/src/core/collection/array/array_set_diff_sorting.d.ts.map +1 -1
  26. package/src/core/collection/array/array_set_diff_sorting.js +4 -1
  27. package/src/core/collection/array/array_shuffle.d.ts.map +1 -1
  28. package/src/core/collection/array/array_shuffle.js +30 -27
  29. package/src/core/collection/array/binarySearchLowIndex.d.ts.map +1 -1
  30. package/src/core/collection/array/binarySearchLowIndex.js +4 -3
  31. package/src/core/collection/array/typed/array_buffer_hash.js +1 -1
  32. package/src/core/collection/array/typed/is_typed_array_equals.d.ts.map +1 -1
  33. package/src/core/collection/array/typed/is_typed_array_equals.js +12 -2
  34. package/src/core/collection/heap/BinaryHeap.d.ts.map +1 -1
  35. package/src/core/collection/heap/BinaryHeap.js +12 -2
  36. package/src/core/collection/queue/Deque.d.ts.map +1 -1
  37. package/src/core/collection/queue/Deque.js +10 -8
  38. package/src/core/collection/table/RowFirstTable.d.ts.map +1 -1
  39. package/src/core/collection/table/RowFirstTable.js +4 -2
  40. package/src/core/collection/table/RowFirstTableSpec.js +2 -2
  41. package/src/core/color/operations/color_lerp.d.ts.map +1 -1
  42. package/src/core/color/operations/color_lerp.js +10 -3
  43. package/src/core/color/rgb2uint32.js +1 -1
  44. package/src/core/color/rgbe9995_to_rgb.js +1 -1
  45. package/src/core/function/objectsEqual.d.ts.map +1 -1
  46. package/src/core/function/objectsEqual.js +2 -1
  47. package/src/core/geom/2d/aabb/AABB2.d.ts.map +1 -1
  48. package/src/core/geom/2d/aabb/AABB2.js +12 -11
  49. package/src/core/geom/2d/convex-hull/convex_hull_jarvis_2d.d.ts.map +1 -1
  50. package/src/core/geom/2d/convex-hull/convex_hull_jarvis_2d.js +30 -4
  51. package/src/core/geom/2d/convex-hull/fixed_convex_hull_relaxation.d.ts.map +1 -1
  52. package/src/core/geom/2d/convex-hull/fixed_convex_hull_relaxation.js +6 -2
  53. package/src/core/geom/2d/hash-grid/SpatialHashGrid.d.ts.map +1 -1
  54. package/src/core/geom/2d/hash-grid/SpatialHashGrid.js +388 -386
  55. package/src/core/geom/2d/hash-grid/shg_query_elements_line.d.ts.map +1 -1
  56. package/src/core/geom/2d/hash-grid/shg_query_elements_line.js +8 -3
  57. package/src/core/geom/2d/quad-tree/QuadTreeDatum.d.ts.map +1 -1
  58. package/src/core/geom/2d/quad-tree/QuadTreeDatum.js +9 -1
  59. package/src/core/geom/2d/quad-tree/qt_query_data_nearest_to_point.d.ts +3 -1
  60. package/src/core/geom/2d/quad-tree/qt_query_data_nearest_to_point.d.ts.map +1 -1
  61. package/src/core/geom/2d/quad-tree/qt_query_data_nearest_to_point.js +3 -1
  62. package/src/core/geom/2d/quad-tree-binary/QuadTree.js +714 -714
  63. package/src/core/geom/2d/r-tree/StaticR2Tree.d.ts.map +1 -1
  64. package/src/core/geom/2d/r-tree/StaticR2Tree.js +5 -4
  65. package/src/core/geom/3d/aabb/aabb3_detailed_volume_intersection.d.ts.map +1 -1
  66. package/src/core/geom/3d/aabb/aabb3_detailed_volume_intersection.js +33 -29
  67. package/src/core/geom/3d/aabb/aabb3_near_distance_to_intersection_ray_segment.d.ts.map +1 -1
  68. package/src/core/geom/3d/aabb/aabb3_near_distance_to_intersection_ray_segment.js +3 -1
  69. package/src/core/geom/3d/aabb/aabb3_signed_distance_to_aabb3.d.ts.map +1 -1
  70. package/src/core/geom/3d/aabb/aabb3_signed_distance_to_aabb3.js +10 -7
  71. package/src/core/geom/3d/aabb/aabb3_transformed_compute_plane_side.d.ts.map +1 -1
  72. package/src/core/geom/3d/aabb/aabb3_transformed_compute_plane_side.js +30 -9
  73. package/src/core/geom/3d/aabb/compute_aabb_from_points.js +3 -3
  74. package/src/core/geom/3d/box/box3_raycast.d.ts +37 -0
  75. package/src/core/geom/3d/box/box3_raycast.d.ts.map +1 -0
  76. package/src/core/geom/3d/box/box3_raycast.js +81 -0
  77. package/src/core/geom/3d/capsule/capsule_raycast.d.ts +35 -0
  78. package/src/core/geom/3d/capsule/capsule_raycast.d.ts.map +1 -0
  79. package/src/core/geom/3d/capsule/capsule_raycast.js +93 -0
  80. package/src/core/geom/3d/cone/compute_bounding_cone_of_2_cones.d.ts.map +1 -1
  81. package/src/core/geom/3d/cone/compute_bounding_cone_of_2_cones.js +4 -0
  82. package/src/core/geom/3d/frustum/frustum3_computeNearestPointToPoint.js +1 -1
  83. package/src/core/geom/3d/line/line3_compute_segment_point_distance_eikonal.d.ts.map +1 -1
  84. package/src/core/geom/3d/line/line3_compute_segment_point_distance_eikonal.js +3 -2
  85. package/src/core/geom/3d/mat4/decompose_matrix_4_array.d.ts.map +1 -1
  86. package/src/core/geom/3d/mat4/decompose_matrix_4_array.js +12 -2
  87. package/src/core/geom/3d/mat4/eulerAnglesFromMatrix.js +2 -2
  88. package/src/core/geom/3d/mat4/m4_multiply_alphatensor.d.ts +1 -1
  89. package/src/core/geom/3d/mat4/m4_multiply_alphatensor.d.ts.map +1 -1
  90. package/src/core/geom/3d/mat4/m4_multiply_alphatensor.js +19 -13
  91. package/src/core/geom/3d/octahedra/octahedral_direction_to_uv.d.ts.map +1 -1
  92. package/src/core/geom/3d/octahedra/octahedral_direction_to_uv.js +3 -2
  93. package/src/core/geom/3d/plane/plane3_compute_plane_intersection.js +3 -2
  94. package/src/core/geom/3d/shape/MeshShape3D.d.ts.map +1 -1
  95. package/src/core/geom/3d/shape/MeshShape3D.js +7 -0
  96. package/src/core/geom/3d/shape/UnionShape3D.d.ts.map +1 -1
  97. package/src/core/geom/3d/shape/UnionShape3D.js +3 -2
  98. package/src/core/geom/3d/shape/util/shape3d_voxelize_to_grid.d.ts.map +1 -1
  99. package/src/core/geom/3d/shape/util/shape3d_voxelize_to_grid.js +153 -148
  100. package/src/core/geom/3d/sphere/harmonics/sh3_dering_optimize_positive.d.ts.map +1 -1
  101. package/src/core/geom/3d/sphere/harmonics/sh3_dering_optimize_positive.js +7 -0
  102. package/src/core/geom/3d/sphere/harmonics/sh3_sample_by_direction.d.ts.map +1 -1
  103. package/src/core/geom/3d/sphere/harmonics/sh3_sample_by_direction.js +13 -10
  104. package/src/core/geom/3d/sphere/sphere_projected_sphere_radius_sqr.d.ts +1 -1
  105. package/src/core/geom/3d/sphere/sphere_projected_sphere_radius_sqr.js +2 -2
  106. package/src/core/geom/3d/sphere/sphere_raycast.d.ts +33 -0
  107. package/src/core/geom/3d/sphere/sphere_raycast.d.ts.map +1 -0
  108. package/src/core/geom/3d/sphere/sphere_raycast.js +47 -0
  109. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_tet_get_neighbours.d.ts +24 -0
  110. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_tet_get_neighbours.d.ts.map +1 -0
  111. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_tet_get_neighbours.js +39 -0
  112. package/src/core/geom/3d/tetrahedra/triangle/trace_triangular_depth_map.d.ts.map +1 -1
  113. package/src/core/geom/3d/tetrahedra/triangle/trace_triangular_depth_map.js +4 -2
  114. package/src/core/geom/3d/topology/bounds/computeTriangleClusterNormalBoundingCone.js +3 -3
  115. package/src/core/geom/3d/topology/simplify/decimate_edge_collapse_snap.js +1 -1
  116. package/src/core/geom/3d/topology/tm_vertex_compute_normal.d.ts.map +1 -1
  117. package/src/core/geom/3d/topology/tm_vertex_compute_normal.js +4 -2
  118. package/src/core/geom/3d/util/make_justified_point_grid.d.ts.map +1 -1
  119. package/src/core/geom/3d/util/make_justified_point_grid.js +18 -10
  120. package/src/core/geom/ConicRay.d.ts.map +1 -1
  121. package/src/core/geom/ConicRay.js +11 -13
  122. package/src/core/geom/packing/max-rect/removeRedundantBoxes.d.ts.map +1 -1
  123. package/src/core/geom/packing/max-rect/removeRedundantBoxes.js +19 -4
  124. package/src/core/geom/vec3/v3_array_copy.d.ts +3 -3
  125. package/src/core/geom/vec3/v3_array_copy.d.ts.map +1 -1
  126. package/src/core/geom/vec3/v3_array_copy.js +2 -2
  127. package/src/core/geom/vec3/v3_cross.d.ts +17 -0
  128. package/src/core/geom/vec3/v3_cross.d.ts.map +1 -0
  129. package/src/core/geom/vec3/v3_cross.js +20 -0
  130. package/src/core/geom/vec3/v3_orthonormal_matrix_from_normal.d.ts.map +1 -0
  131. package/src/{engine/graphics/sh3/path_tracer/sampling → core/geom/vec3}/v3_orthonormal_matrix_from_normal.js +1 -1
  132. package/src/core/geom/vec3/v3_subtract.d.ts +16 -0
  133. package/src/core/geom/vec3/v3_subtract.d.ts.map +1 -0
  134. package/src/core/geom/vec3/v3_subtract.js +19 -0
  135. package/src/core/graph/coloring/colorizeGraph.js +2 -2
  136. package/src/core/graph/csr/CSRGraph.d.ts.map +1 -1
  137. package/src/core/graph/csr/CSRGraph.js +325 -319
  138. package/src/core/graph/layout/CircleLayout.d.ts.map +1 -1
  139. package/src/core/graph/layout/CircleLayout.js +8 -6
  140. package/src/core/graph/metis/native/refine/compute_kway_params.d.ts.map +1 -1
  141. package/src/core/graph/metis/native/refine/compute_kway_params.js +139 -138
  142. package/src/core/graph/mn_graph_coarsen.d.ts.map +1 -1
  143. package/src/core/graph/mn_graph_coarsen.js +4 -2
  144. package/src/core/graph/v2/NodeContainer.js +7 -7
  145. package/src/core/localization/LocalizationEngine.js +1 -1
  146. package/src/core/math/bell_membership_function.d.ts.map +1 -1
  147. package/src/core/math/bell_membership_function.js +3 -1
  148. package/src/core/math/complex/complex_add.d.ts +4 -4
  149. package/src/core/math/complex/complex_add.d.ts.map +1 -1
  150. package/src/core/math/complex/complex_add.js +3 -3
  151. package/src/core/math/complex/complex_div.d.ts +4 -4
  152. package/src/core/math/complex/complex_div.d.ts.map +1 -1
  153. package/src/core/math/complex/complex_div.js +3 -3
  154. package/src/core/math/complex/complex_mul.d.ts +4 -4
  155. package/src/core/math/complex/complex_mul.d.ts.map +1 -1
  156. package/src/core/math/complex/complex_mul.js +3 -3
  157. package/src/core/math/complex/complex_sub.d.ts +4 -4
  158. package/src/core/math/complex/complex_sub.d.ts.map +1 -1
  159. package/src/core/math/complex/complex_sub.js +3 -3
  160. package/src/core/math/idct_1d.d.ts +4 -4
  161. package/src/core/math/idct_1d.d.ts.map +1 -1
  162. package/src/core/math/idct_1d.js +3 -3
  163. package/src/core/math/noise/create_simplex_noise_2d.d.ts.map +1 -1
  164. package/src/core/math/noise/create_simplex_noise_2d.js +4 -2
  165. package/src/core/math/noise/sdnoise.d.ts.map +1 -1
  166. package/src/core/math/noise/sdnoise.js +12 -9
  167. package/src/core/math/physics/mie/compute_bhmie_optical_properties.d.ts.map +1 -1
  168. package/src/core/math/physics/mie/compute_bhmie_optical_properties.js +94 -50
  169. package/src/core/math/physics/mie/lorenz_mie_coefs.d.ts +3 -6
  170. package/src/core/math/physics/mie/lorenz_mie_coefs.d.ts.map +1 -1
  171. package/src/core/math/physics/mie/lorenz_mie_coefs.js +180 -157
  172. package/src/core/math/physics/mie/mie_ab_to_optical_properties.d.ts +3 -4
  173. package/src/core/math/physics/mie/mie_ab_to_optical_properties.d.ts.map +1 -1
  174. package/src/core/math/physics/mie/mie_ab_to_optical_properties.js +47 -21
  175. package/src/core/math/random/randomIntegerBetween.d.ts.map +1 -1
  176. package/src/core/math/random/randomIntegerBetween.js +4 -1
  177. package/src/core/math/solveCubic.d.ts.map +1 -1
  178. package/src/core/math/solveCubic.js +95 -82
  179. package/src/core/math/spline/computeCatmullRomSplineUniformDistance.d.ts.map +1 -1
  180. package/src/core/math/spline/computeCatmullRomSplineUniformDistance.js +13 -0
  181. package/src/core/math/statistics/softmax.js +1 -1
  182. package/src/core/model/node-graph/visual/NodeGraphVisualData.d.ts +1 -0
  183. package/src/core/model/node-graph/visual/NodeGraphVisualData.d.ts.map +1 -1
  184. package/src/core/model/node-graph/visual/NodeGraphVisualData.js +2 -1
  185. package/src/core/model/node-graph/visual/NodeVisualData.js +1 -1
  186. package/src/core/model/object/ImmutableObjectPool.d.ts +7 -0
  187. package/src/core/model/object/ImmutableObjectPool.d.ts.map +1 -1
  188. package/src/core/model/object/ImmutableObjectPool.js +20 -10
  189. package/src/core/model/reactive/evaluation/MultiPredicateEvaluator.d.ts.map +1 -1
  190. package/src/core/model/reactive/evaluation/MultiPredicateEvaluator.js +39 -2
  191. package/src/core/model/reactive/model/terminal/ReactiveReference.d.ts.map +1 -1
  192. package/src/core/model/reactive/model/terminal/ReactiveReference.js +2 -0
  193. package/src/core/parser/simple/readHexToken.d.ts.map +1 -1
  194. package/src/core/parser/simple/readHexToken.js +6 -0
  195. package/src/core/primitives/numbers/number_pretty_print.d.ts.map +1 -1
  196. package/src/core/primitives/numbers/number_pretty_print.js +4 -1
  197. package/src/core/primitives/strings/string_jaro_winkler.js +1 -1
  198. package/src/core/process/CompositeProcess.js +1 -1
  199. package/src/core/process/action/AsynchronousDelayAction.d.ts.map +1 -1
  200. package/src/core/process/action/AsynchronousDelayAction.js +3 -0
  201. package/src/core/process/executor/ConcurrentExecutor.d.ts.map +1 -1
  202. package/src/core/process/executor/ConcurrentExecutor.js +3 -2
  203. package/src/core/process/task/util/randomCountTask.d.ts.map +1 -1
  204. package/src/core/process/task/util/randomCountTask.js +3 -1
  205. package/src/core/process/undo/ActionProcessor.d.ts.map +1 -1
  206. package/src/core/process/undo/ActionProcessor.js +5 -3
  207. package/src/core/process/worker/WorkerBuilder.js +3 -3
  208. package/src/engine/animation/curve/AnimationCurve.d.ts.map +1 -1
  209. package/src/engine/animation/curve/AnimationCurve.js +4 -2
  210. package/src/engine/control/first-person/DESIGN.md +1 -1
  211. package/src/engine/control/first-person/FirstPersonMotionPhase.d.ts +55 -0
  212. package/src/engine/control/first-person/FirstPersonMotionPhase.d.ts.map +1 -0
  213. package/src/engine/control/first-person/FirstPersonMotionPhase.js +134 -0
  214. package/src/engine/control/first-person/FirstPersonPlayerController.d.ts +23 -2
  215. package/src/engine/control/first-person/FirstPersonPlayerController.d.ts.map +1 -1
  216. package/src/engine/control/first-person/FirstPersonPlayerController.js +1 -1
  217. package/src/engine/control/first-person/FirstPersonPlayerControllerConfig.d.ts +168 -0
  218. package/src/engine/control/first-person/FirstPersonPlayerControllerConfig.d.ts.map +1 -1
  219. package/src/engine/control/first-person/FirstPersonPlayerControllerConfig.js +115 -0
  220. package/src/engine/control/first-person/FirstPersonPlayerControllerSystem.d.ts +71 -0
  221. package/src/engine/control/first-person/FirstPersonPlayerControllerSystem.d.ts.map +1 -1
  222. package/src/engine/control/first-person/FirstPersonPlayerControllerSystem.js +255 -55
  223. package/src/engine/control/first-person/abilities/LedgeGrab.d.ts +82 -43
  224. package/src/engine/control/first-person/abilities/LedgeGrab.d.ts.map +1 -1
  225. package/src/engine/control/first-person/abilities/LedgeGrab.js +405 -213
  226. package/src/engine/control/first-person/abilities/Mantle.d.ts +6 -0
  227. package/src/engine/control/first-person/abilities/Mantle.d.ts.map +1 -1
  228. package/src/engine/control/first-person/abilities/Mantle.js +104 -45
  229. package/src/engine/control/first-person/abilities/ScrambleUp.d.ts +61 -0
  230. package/src/engine/control/first-person/abilities/ScrambleUp.d.ts.map +1 -0
  231. package/src/engine/control/first-person/abilities/ScrambleUp.js +182 -0
  232. package/src/engine/control/first-person/math/jumpDynamics.d.ts +84 -0
  233. package/src/engine/control/first-person/math/jumpDynamics.d.ts.map +1 -0
  234. package/src/engine/control/first-person/math/jumpDynamics.js +108 -0
  235. package/src/engine/control/first-person/prototype_first_person_controller.js +45 -1
  236. package/src/engine/graphics/camera/testClippingPlaneComputation.js +1 -1
  237. package/src/engine/graphics/ecs/decal/v2/FPDecalSystem.d.ts.map +1 -1
  238. package/src/engine/graphics/ecs/decal/v2/FPDecalSystem.js +8 -0
  239. package/src/engine/graphics/ecs/path/tube/prototypeAnimatedPathMask.js +1 -1
  240. package/src/engine/graphics/particles/particular/engine/utils/volume/prototypeParticleVolume.js +1 -1
  241. package/src/engine/graphics/render/buffer/buffers/prototypeNormalFrameBuffer.js +1 -1
  242. package/src/engine/graphics/render/forward_plus/plugin/ptototypeFPPlugin.js +1 -1
  243. package/src/engine/graphics/render/visibility/hiz/prototypeHiZ.js +1 -1
  244. package/src/engine/graphics/sh3/path_tracer/texture/sample_material.js +1 -1
  245. package/src/engine/graphics/shadows/testShadowMapRendering.js +1 -1
  246. package/src/engine/physics/CONSTRAINT_SOLVER_BENCH_LOG.md +208 -0
  247. package/src/engine/physics/CONSTRAINT_SOLVER_IMPROVEMENTS_PLAN.md +364 -0
  248. package/src/engine/physics/PLAN.md +6 -5
  249. package/src/engine/physics/constraint/solve_constraints.d.ts +4 -1
  250. package/src/engine/physics/constraint/solve_constraints.d.ts.map +1 -1
  251. package/src/engine/physics/constraint/solve_constraints.js +147 -33
  252. package/src/engine/physics/ecs/PhysicsSystem.d.ts.map +1 -1
  253. package/src/engine/physics/ecs/PhysicsSystem.js +1750 -1747
  254. package/src/engine/physics/fluid/ecs/FluidSystem.d.ts +3 -3
  255. package/src/engine/physics/gjk/gjk_epa_penetration.d.ts +12 -8
  256. package/src/engine/physics/gjk/gjk_epa_penetration.d.ts.map +1 -1
  257. package/src/engine/physics/gjk/gjk_epa_penetration.js +447 -158
  258. package/src/engine/physics/narrowphase/convex_convex_manifold.d.ts.map +1 -1
  259. package/src/engine/physics/narrowphase/convex_convex_manifold.js +22 -25
  260. package/src/engine/physics/narrowphase/convex_decomposition.d.ts +32 -13
  261. package/src/engine/physics/narrowphase/convex_decomposition.d.ts.map +1 -1
  262. package/src/engine/physics/narrowphase/convex_decomposition.js +61 -65
  263. package/src/engine/physics/narrowphase/mesh_convex_hull.d.ts.map +1 -1
  264. package/src/engine/physics/narrowphase/mesh_convex_hull.js +13 -8
  265. package/src/engine/physics/narrowphase/refine_ray_concave.d.ts.map +1 -1
  266. package/src/engine/physics/narrowphase/refine_ray_concave.js +5 -3
  267. package/src/engine/physics/narrowphase/refine_ray_hit.d.ts.map +1 -1
  268. package/src/engine/physics/narrowphase/refine_ray_hit.js +81 -78
  269. package/src/engine/sound/SoundEngine.d.ts.map +1 -1
  270. package/src/engine/sound/SoundEngine.js +28 -0
  271. package/src/engine/sound/dB2Volume.d.ts +1 -1
  272. package/src/engine/sound/dB2Volume.d.ts.map +1 -1
  273. package/src/engine/sound/dB2Volume.js +1 -1
  274. package/src/engine/sound/ecs/SoundController.d.ts +4 -0
  275. package/src/engine/sound/ecs/SoundController.d.ts.map +1 -1
  276. package/src/engine/sound/ecs/SoundController.js +4 -0
  277. package/src/engine/sound/ecs/SoundControllerSystem.d.ts +5 -0
  278. package/src/engine/sound/ecs/SoundControllerSystem.d.ts.map +1 -1
  279. package/src/engine/sound/ecs/SoundControllerSystem.js +5 -0
  280. package/src/engine/sound/ecs/audio/AudioEmitter.d.ts +69 -0
  281. package/src/engine/sound/ecs/audio/AudioEmitter.d.ts.map +1 -0
  282. package/src/engine/sound/ecs/audio/AudioEmitter.js +83 -0
  283. package/src/engine/sound/ecs/audio/AudioEmitterSystem.d.ts +97 -0
  284. package/src/engine/sound/ecs/audio/AudioEmitterSystem.d.ts.map +1 -0
  285. package/src/engine/sound/ecs/audio/AudioEmitterSystem.js +238 -0
  286. package/src/engine/sound/ecs/audio/LiveEmitterSet.d.ts +90 -0
  287. package/src/engine/sound/ecs/audio/LiveEmitterSet.d.ts.map +1 -0
  288. package/src/engine/sound/ecs/audio/LiveEmitterSet.js +324 -0
  289. package/src/engine/sound/ecs/audio/SpatialAudioIndex.d.ts +59 -0
  290. package/src/engine/sound/ecs/audio/SpatialAudioIndex.d.ts.map +1 -0
  291. package/src/engine/sound/ecs/audio/SpatialAudioIndex.js +140 -0
  292. package/src/engine/sound/ecs/emitter/SoundEmitter.d.ts +16 -65
  293. package/src/engine/sound/ecs/emitter/SoundEmitter.d.ts.map +1 -1
  294. package/src/engine/sound/ecs/emitter/SoundEmitter.js +19 -224
  295. package/src/engine/sound/ecs/emitter/SoundEmitterComponentContext.d.ts +26 -29
  296. package/src/engine/sound/ecs/emitter/SoundEmitterComponentContext.d.ts.map +1 -1
  297. package/src/engine/sound/ecs/emitter/SoundEmitterComponentContext.js +168 -135
  298. package/src/engine/sound/ecs/emitter/SoundEmitterSystem.d.ts +36 -59
  299. package/src/engine/sound/ecs/emitter/SoundEmitterSystem.d.ts.map +1 -1
  300. package/src/engine/sound/ecs/emitter/SoundEmitterSystem.js +154 -390
  301. package/src/engine/sound/ecs/emitter/SoundTrack.d.ts +20 -23
  302. package/src/engine/sound/ecs/emitter/SoundTrack.d.ts.map +1 -1
  303. package/src/engine/sound/ecs/emitter/SoundTrack.js +34 -152
  304. package/src/engine/sound/sopra/IMPLEMENTATION_PLAN.md +993 -0
  305. package/src/engine/sound/sopra/README.md +643 -7
  306. package/src/engine/sound/sopra/SopraEngine.d.ts +229 -0
  307. package/src/engine/sound/sopra/SopraEngine.d.ts.map +1 -0
  308. package/src/engine/sound/sopra/SopraEngine.js +423 -0
  309. package/src/engine/sound/sopra/asset/AssetManagerBufferProvider.d.ts +26 -0
  310. package/src/engine/sound/sopra/asset/AssetManagerBufferProvider.d.ts.map +1 -0
  311. package/src/engine/sound/sopra/asset/AssetManagerBufferProvider.js +71 -0
  312. package/src/engine/sound/sopra/asset/BufferProvider.d.ts +24 -0
  313. package/src/engine/sound/sopra/asset/BufferProvider.d.ts.map +1 -0
  314. package/src/engine/sound/sopra/asset/BufferProvider.js +29 -0
  315. package/src/engine/sound/sopra/asset/StubBufferProvider.d.ts +31 -0
  316. package/src/engine/sound/sopra/asset/StubBufferProvider.d.ts.map +1 -0
  317. package/src/engine/sound/sopra/asset/StubBufferProvider.js +58 -0
  318. package/src/engine/sound/sopra/definition/BusDefinition.d.ts +83 -0
  319. package/src/engine/sound/sopra/definition/BusDefinition.d.ts.map +1 -0
  320. package/src/engine/sound/sopra/definition/BusDefinition.js +142 -0
  321. package/src/engine/sound/sopra/definition/BusDefinitionSerializationAdapter.d.ts +17 -0
  322. package/src/engine/sound/sopra/definition/BusDefinitionSerializationAdapter.d.ts.map +1 -0
  323. package/src/engine/sound/sopra/definition/BusDefinitionSerializationAdapter.js +54 -0
  324. package/src/engine/sound/sopra/definition/DuckingRule.d.ts +71 -0
  325. package/src/engine/sound/sopra/definition/DuckingRule.d.ts.map +1 -0
  326. package/src/engine/sound/sopra/definition/DuckingRule.js +106 -0
  327. package/src/engine/sound/sopra/definition/DuckingRuleSerializationAdapter.d.ts +18 -0
  328. package/src/engine/sound/sopra/definition/DuckingRuleSerializationAdapter.d.ts.map +1 -0
  329. package/src/engine/sound/sopra/definition/DuckingRuleSerializationAdapter.js +31 -0
  330. package/src/engine/sound/sopra/definition/EventDescription.d.ts +132 -0
  331. package/src/engine/sound/sopra/definition/EventDescription.d.ts.map +1 -0
  332. package/src/engine/sound/sopra/definition/EventDescription.js +259 -0
  333. package/src/engine/sound/sopra/definition/EventDescriptionSerializationAdapter.d.ts +17 -0
  334. package/src/engine/sound/sopra/definition/EventDescriptionSerializationAdapter.d.ts.map +1 -0
  335. package/src/engine/sound/sopra/definition/EventDescriptionSerializationAdapter.js +71 -0
  336. package/src/engine/sound/sopra/definition/MixerSnapshot.d.ts +51 -0
  337. package/src/engine/sound/sopra/definition/MixerSnapshot.d.ts.map +1 -0
  338. package/src/engine/sound/sopra/definition/MixerSnapshot.js +83 -0
  339. package/src/engine/sound/sopra/definition/MixerSnapshotSerializationAdapter.d.ts +18 -0
  340. package/src/engine/sound/sopra/definition/MixerSnapshotSerializationAdapter.d.ts.map +1 -0
  341. package/src/engine/sound/sopra/definition/MixerSnapshotSerializationAdapter.js +39 -0
  342. package/src/engine/sound/sopra/definition/ParameterDefinition.d.ts +72 -0
  343. package/src/engine/sound/sopra/definition/ParameterDefinition.d.ts.map +1 -0
  344. package/src/engine/sound/sopra/definition/ParameterDefinition.js +117 -0
  345. package/src/engine/sound/sopra/definition/ParameterDefinitionSerializationAdapter.d.ts +18 -0
  346. package/src/engine/sound/sopra/definition/ParameterDefinitionSerializationAdapter.d.ts.map +1 -0
  347. package/src/engine/sound/sopra/definition/ParameterDefinitionSerializationAdapter.js +31 -0
  348. package/src/engine/sound/sopra/definition/SopraPanningModel.d.ts +14 -0
  349. package/src/engine/sound/sopra/definition/SopraPanningModel.d.ts.map +1 -0
  350. package/src/engine/sound/sopra/definition/SopraPanningModel.js +20 -0
  351. package/src/engine/sound/sopra/definition/VoiceStealMode.d.ts +10 -0
  352. package/src/engine/sound/sopra/definition/VoiceStealMode.d.ts.map +1 -0
  353. package/src/engine/sound/sopra/definition/VoiceStealMode.js +18 -0
  354. package/src/engine/sound/sopra/definition/clip/AbstractAudioClip.d.ts +93 -0
  355. package/src/engine/sound/sopra/definition/clip/AbstractAudioClip.d.ts.map +1 -0
  356. package/src/engine/sound/sopra/definition/clip/AbstractAudioClip.js +109 -0
  357. package/src/engine/sound/sopra/definition/clip/BlendContainerAudioClip.d.ts +80 -0
  358. package/src/engine/sound/sopra/definition/clip/BlendContainerAudioClip.d.ts.map +1 -0
  359. package/src/engine/sound/sopra/definition/clip/BlendContainerAudioClip.js +181 -0
  360. package/src/engine/sound/sopra/definition/clip/BlendContainerAudioClipSerializationAdapter.d.ts +17 -0
  361. package/src/engine/sound/sopra/definition/clip/BlendContainerAudioClipSerializationAdapter.d.ts.map +1 -0
  362. package/src/engine/sound/sopra/definition/clip/BlendContainerAudioClipSerializationAdapter.js +74 -0
  363. package/src/engine/sound/sopra/definition/clip/ContainerAudioClip.d.ts +34 -0
  364. package/src/engine/sound/sopra/definition/clip/ContainerAudioClip.d.ts.map +1 -0
  365. package/src/engine/sound/sopra/definition/clip/ContainerAudioClip.js +100 -0
  366. package/src/engine/sound/sopra/definition/clip/RandomContainerAudioClip.d.ts +101 -0
  367. package/src/engine/sound/sopra/definition/clip/RandomContainerAudioClip.d.ts.map +1 -0
  368. package/src/engine/sound/sopra/definition/clip/RandomContainerAudioClip.js +230 -0
  369. package/src/engine/sound/sopra/definition/clip/RandomContainerAudioClipSerializationAdapter.d.ts +17 -0
  370. package/src/engine/sound/sopra/definition/clip/RandomContainerAudioClipSerializationAdapter.d.ts.map +1 -0
  371. package/src/engine/sound/sopra/definition/clip/RandomContainerAudioClipSerializationAdapter.js +54 -0
  372. package/src/engine/sound/sopra/definition/clip/SampleAudioClip.d.ts +103 -0
  373. package/src/engine/sound/sopra/definition/clip/SampleAudioClip.d.ts.map +1 -0
  374. package/src/engine/sound/sopra/definition/clip/SampleAudioClip.js +191 -0
  375. package/src/engine/sound/sopra/definition/clip/SampleAudioClipSerializationAdapter.d.ts +18 -0
  376. package/src/engine/sound/sopra/definition/clip/SampleAudioClipSerializationAdapter.d.ts.map +1 -0
  377. package/src/engine/sound/sopra/definition/clip/SampleAudioClipSerializationAdapter.js +39 -0
  378. package/src/engine/sound/sopra/definition/clip/SequenceContainerAudioClip.d.ts +40 -0
  379. package/src/engine/sound/sopra/definition/clip/SequenceContainerAudioClip.d.ts.map +1 -0
  380. package/src/engine/sound/sopra/definition/clip/SequenceContainerAudioClip.js +91 -0
  381. package/src/engine/sound/sopra/definition/clip/SequenceContainerAudioClipSerializationAdapter.d.ts +17 -0
  382. package/src/engine/sound/sopra/definition/clip/SequenceContainerAudioClipSerializationAdapter.d.ts.map +1 -0
  383. package/src/engine/sound/sopra/definition/clip/SequenceContainerAudioClipSerializationAdapter.js +42 -0
  384. package/src/engine/sound/sopra/definition/clip/SilenceAudioClip.d.ts +44 -0
  385. package/src/engine/sound/sopra/definition/clip/SilenceAudioClip.d.ts.map +1 -0
  386. package/src/engine/sound/sopra/definition/clip/SilenceAudioClip.js +77 -0
  387. package/src/engine/sound/sopra/definition/clip/SilenceAudioClipSerializationAdapter.d.ts +18 -0
  388. package/src/engine/sound/sopra/definition/clip/SilenceAudioClipSerializationAdapter.d.ts.map +1 -0
  389. package/src/engine/sound/sopra/definition/clip/SilenceAudioClipSerializationAdapter.js +27 -0
  390. package/src/engine/sound/sopra/definition/clip/SwitchContainerAudioClip.d.ts +65 -0
  391. package/src/engine/sound/sopra/definition/clip/SwitchContainerAudioClip.d.ts.map +1 -0
  392. package/src/engine/sound/sopra/definition/clip/SwitchContainerAudioClip.js +131 -0
  393. package/src/engine/sound/sopra/definition/clip/SwitchContainerAudioClipSerializationAdapter.d.ts +17 -0
  394. package/src/engine/sound/sopra/definition/clip/SwitchContainerAudioClipSerializationAdapter.d.ts.map +1 -0
  395. package/src/engine/sound/sopra/definition/clip/SwitchContainerAudioClipSerializationAdapter.js +41 -0
  396. package/src/engine/sound/sopra/definition/effect/AbstractAudioEffect.d.ts +24 -0
  397. package/src/engine/sound/sopra/definition/effect/AbstractAudioEffect.d.ts.map +1 -0
  398. package/src/engine/sound/sopra/definition/effect/AbstractAudioEffect.js +24 -0
  399. package/src/engine/sound/sopra/definition/effect/CompressorEffect.d.ts +70 -0
  400. package/src/engine/sound/sopra/definition/effect/CompressorEffect.d.ts.map +1 -0
  401. package/src/engine/sound/sopra/definition/effect/CompressorEffect.js +120 -0
  402. package/src/engine/sound/sopra/definition/effect/CompressorEffectSerializationAdapter.d.ts +18 -0
  403. package/src/engine/sound/sopra/definition/effect/CompressorEffectSerializationAdapter.d.ts.map +1 -0
  404. package/src/engine/sound/sopra/definition/effect/CompressorEffectSerializationAdapter.js +31 -0
  405. package/src/engine/sound/sopra/definition/effect/EqEffect.d.ts +74 -0
  406. package/src/engine/sound/sopra/definition/effect/EqEffect.d.ts.map +1 -0
  407. package/src/engine/sound/sopra/definition/effect/EqEffect.js +128 -0
  408. package/src/engine/sound/sopra/definition/effect/EqEffectSerializationAdapter.d.ts +18 -0
  409. package/src/engine/sound/sopra/definition/effect/EqEffectSerializationAdapter.d.ts.map +1 -0
  410. package/src/engine/sound/sopra/definition/effect/EqEffectSerializationAdapter.js +29 -0
  411. package/src/engine/sound/sopra/definition/effect/ReverbEffect.d.ts +49 -0
  412. package/src/engine/sound/sopra/definition/effect/ReverbEffect.d.ts.map +1 -0
  413. package/src/engine/sound/sopra/definition/effect/ReverbEffect.js +101 -0
  414. package/src/engine/sound/sopra/definition/effect/ReverbEffectSerializationAdapter.d.ts +18 -0
  415. package/src/engine/sound/sopra/definition/effect/ReverbEffectSerializationAdapter.d.ts.map +1 -0
  416. package/src/engine/sound/sopra/definition/effect/ReverbEffectSerializationAdapter.js +25 -0
  417. package/src/engine/sound/sopra/legacy/soundEmitterToEventDescription.d.ts +31 -0
  418. package/src/engine/sound/sopra/legacy/soundEmitterToEventDescription.d.ts.map +1 -0
  419. package/src/engine/sound/sopra/legacy/soundEmitterToEventDescription.js +106 -0
  420. package/src/engine/sound/sopra/runtime/BusGraph.d.ts +79 -0
  421. package/src/engine/sound/sopra/runtime/BusGraph.d.ts.map +1 -0
  422. package/src/engine/sound/sopra/runtime/BusGraph.js +227 -0
  423. package/src/engine/sound/sopra/runtime/EventInstance.d.ts +144 -0
  424. package/src/engine/sound/sopra/runtime/EventInstance.d.ts.map +1 -0
  425. package/src/engine/sound/sopra/runtime/EventInstance.js +579 -0
  426. package/src/engine/sound/sopra/runtime/ParameterStore.d.ts +42 -0
  427. package/src/engine/sound/sopra/runtime/ParameterStore.d.ts.map +1 -0
  428. package/src/engine/sound/sopra/runtime/ParameterStore.js +98 -0
  429. package/src/engine/sound/sopra/runtime/SopraPlaybackContext.d.ts +42 -0
  430. package/src/engine/sound/sopra/runtime/SopraPlaybackContext.d.ts.map +1 -0
  431. package/src/engine/sound/sopra/runtime/SopraPlaybackContext.js +68 -0
  432. package/src/engine/sound/sopra/runtime/Voice.d.ts +67 -0
  433. package/src/engine/sound/sopra/runtime/Voice.d.ts.map +1 -0
  434. package/src/engine/sound/sopra/runtime/Voice.js +145 -0
  435. package/src/engine/sound/sopra/runtime/VoiceManager.d.ts +38 -0
  436. package/src/engine/sound/sopra/runtime/VoiceManager.d.ts.map +1 -0
  437. package/src/engine/sound/sopra/runtime/VoiceManager.js +136 -0
  438. package/src/engine/sound/sopra/runtime/VoicePool.d.ts +12 -0
  439. package/src/engine/sound/sopra/runtime/VoicePool.d.ts.map +1 -0
  440. package/src/engine/sound/sopra/runtime/VoicePool.js +17 -0
  441. package/src/engine/sound/sopra/serialization/populateSopraSerializationRegistry.d.ts +11 -0
  442. package/src/engine/sound/sopra/serialization/populateSopraSerializationRegistry.d.ts.map +1 -0
  443. package/src/engine/sound/sopra/serialization/populateSopraSerializationRegistry.js +42 -0
  444. package/src/engine/sound/sopra/serialization/sopraJSON.d.ts +33 -0
  445. package/src/engine/sound/sopra/serialization/sopraJSON.d.ts.map +1 -0
  446. package/src/engine/sound/sopra/serialization/sopraJSON.js +99 -0
  447. package/src/engine/sound/sopra/serialization/sopraSerializationHarness.d.ts +27 -0
  448. package/src/engine/sound/sopra/serialization/sopraSerializationHarness.d.ts.map +1 -0
  449. package/src/engine/sound/sopra/serialization/sopraSerializationHarness.js +49 -0
  450. package/src/engine/sound/sopra/util/MockAudioContext.d.ts +74 -0
  451. package/src/engine/sound/sopra/util/MockAudioContext.d.ts.map +1 -0
  452. package/src/engine/sound/sopra/util/MockAudioContext.js +215 -0
  453. package/src/engine/sound/sopra/util/buildAttenuationCurve.d.ts +15 -0
  454. package/src/engine/sound/sopra/util/buildAttenuationCurve.d.ts.map +1 -0
  455. package/src/engine/sound/sopra/util/buildAttenuationCurve.js +40 -0
  456. package/src/engine/sound/sopra/util/fadeOutAndStop.d.ts +34 -0
  457. package/src/engine/sound/sopra/util/fadeOutAndStop.d.ts.map +1 -0
  458. package/src/engine/sound/sopra/util/fadeOutAndStop.js +60 -0
  459. package/src/engine/sound/volume2dB.d.ts +1 -1
  460. package/src/engine/sound/volume2dB.d.ts.map +1 -1
  461. package/src/engine/sound/volume2dB.js +1 -1
  462. package/src/engine/graphics/sh3/path_tracer/sampling/v3_orthonormal_matrix_from_normal.d.ts.map +0 -1
  463. package/src/engine/physics/narrowphase/ray_shapes.d.ts +0 -66
  464. package/src/engine/physics/narrowphase/ray_shapes.d.ts.map +0 -1
  465. package/src/engine/physics/narrowphase/ray_shapes.js +0 -187
  466. package/src/engine/sound/ecs/emitter/SoundEmitterChannel.d.ts +0 -23
  467. package/src/engine/sound/ecs/emitter/SoundEmitterChannel.d.ts.map +0 -1
  468. package/src/engine/sound/ecs/emitter/SoundEmitterChannel.js +0 -32
  469. package/src/engine/sound/ecs/emitter/SoundTrackNodes.d.ts +0 -18
  470. package/src/engine/sound/ecs/emitter/SoundTrackNodes.d.ts.map +0 -1
  471. package/src/engine/sound/ecs/emitter/SoundTrackNodes.js +0 -18
  472. package/src/engine/sound/sopra/AbstractAudioClip.d.ts +0 -26
  473. package/src/engine/sound/sopra/AbstractAudioClip.d.ts.map +0 -1
  474. package/src/engine/sound/sopra/AbstractAudioClip.js +0 -29
  475. package/src/engine/sound/sopra/ContainerAudioClip.d.ts +0 -12
  476. package/src/engine/sound/sopra/ContainerAudioClip.d.ts.map +0 -1
  477. package/src/engine/sound/sopra/ContainerAudioClip.js +0 -13
  478. package/src/engine/sound/sopra/RandomContainerAudioClip.d.ts +0 -12
  479. package/src/engine/sound/sopra/RandomContainerAudioClip.d.ts.map +0 -1
  480. package/src/engine/sound/sopra/RandomContainerAudioClip.js +0 -15
  481. package/src/engine/sound/sopra/SequenceContainerAudioClip.d.ts +0 -7
  482. package/src/engine/sound/sopra/SequenceContainerAudioClip.d.ts.map +0 -1
  483. package/src/engine/sound/sopra/SequenceContainerAudioClip.js +0 -8
  484. package/src/engine/sound/sopra/SilenceAudioClip.d.ts +0 -13
  485. package/src/engine/sound/sopra/SilenceAudioClip.d.ts.map +0 -1
  486. package/src/engine/sound/sopra/SilenceAudioClip.js +0 -15
  487. /package/src/{engine/graphics/sh3/path_tracer/sampling → core/geom/vec3}/v3_orthonormal_matrix_from_normal.d.ts +0 -0
@@ -1,3 +1,11 @@
1
+ import { v3_compute_triangle_normal } from "../../../core/geom/3d/triangle/v3_compute_triangle_normal.js";
2
+ import { v3_array_copy } from "../../../core/geom/vec3/v3_array_copy.js";
3
+ import { v3_cross } from "../../../core/geom/vec3/v3_cross.js";
4
+ import { v3_dot } from "../../../core/geom/vec3/v3_dot.js";
5
+ import { v3_length_sqr } from "../../../core/geom/vec3/v3_length_sqr.js";
6
+ import { v3_subtract } from "../../../core/geom/vec3/v3_subtract.js";
7
+ import { v3_triple_cross_product } from "../../../core/geom/vec3/v3_triple_cross_product.js";
8
+
1
9
  /**
2
10
  * Robust GJK + EPA penetration for convex shapes — the narrowphase's
3
11
  * convex-fallback and concave-per-triangle penetration query. Replaced the
@@ -13,11 +21,13 @@
13
21
  * difference's closest face to the origin. No cross-frame state → reset-and-
14
22
  * resimulate determinism preserved.
15
23
  *
16
- * Even with per-call allocation (plain arrays) it measured ~2.1× FASTER than the
17
- * replaced pair, which wasted all 64 iterations diverging on the degenerate
18
- * simplex. KNOWN FOLLOW-UP: port the hot path to typed-array pools to remove the
19
- * per-call allocation; the convex/concave EPA fallbacks are rarely hit today
20
- * (closed-form solvers + SAT cover the common pairs), so it has not been urgent.
24
+ * Allocation-free hot path: the GJK simplex lives in a flat `Float64Array(12)`
25
+ * and the EPA polytope in pooled flat typed arrays (vertices, face vertex-indices,
26
+ * face normals, face distances, horizon edges). All vector math goes through
27
+ * `v3_*` out-parameter helpers writing into module scratch no per-call arrays
28
+ * or face objects. The pools are module-level scratch reused across calls; the
29
+ * engine is single-threaded and GJK/EPA is never re-entered, matching the rest
30
+ * of the physics core's scratch convention.
21
31
  *
22
32
  * @author Alex Goldring
23
33
  * @copyright Company Named Limited (c) 2026
@@ -26,25 +36,55 @@
26
36
  const GJK_MAX_ITER = 64;
27
37
  const EPA_MAX_ITER = 64;
28
38
  const EPA_EPS = 1e-8;
39
+ const DIR_EPS = 1e-18;
40
+
41
+ // EPA pool capacities. EPA adds at most one vertex per iteration, so vertices are
42
+ // bounded by 4 + EPA_MAX_ITER (= 68), faces by Euler's 2V−4 (= 132) and horizon
43
+ // edges by 3V−6 (= 198). These caps clear those bounds with margin, so for the
44
+ // convex inputs the narrowphase feeds they are never reached; the guards below
45
+ // degrade to "use the current best face" purely as a defence against degenerate
46
+ // input rather than growing the pools.
47
+ const VERT_CAP = 128;
48
+ const FACE_CAP = 256;
49
+ const EDGE_CAP = 256;
29
50
 
51
+ // --- support scratch ---
30
52
  const _sA = new Float64Array(3);
31
53
  const _sB = new Float64Array(3);
54
+ const _w = new Float64Array(3); // newly sampled support point
32
55
 
33
- function sub(a, b) { return [a[0] - b[0], a[1] - b[1], a[2] - b[2]]; }
34
- function dot(a, b) { return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; }
35
- function cross(a, b) {
36
- return [a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]];
37
- }
38
- function neg(a) { return [-a[0], -a[1], -a[2]]; }
39
- function lensq(a) { return a[0] * a[0] + a[1] * a[1] + a[2] * a[2]; }
40
- // triple product (a×b)×c = b(a·c) − a(b·c)
41
- function tripleProduct(a, b, c) {
42
- const ac = dot(a, c), bc = dot(b, c);
43
- return [b[0] * ac - a[0] * bc, b[1] * ac - a[1] * bc, b[2] * ac - a[2] * bc];
44
- }
56
+ // --- GJK simplex + search direction ---
57
+ const SIMPLEX = new Float64Array(12); // 4 points, 3 floats each; slot 0 = newest (a)
58
+ const DIR = new Float64Array(3);
59
+
60
+ // --- vector scratch (shared; GJK and EPA never run concurrently) ---
61
+ const _ab = new Float64Array(3);
62
+ const _ac = new Float64Array(3);
63
+ const _ad = new Float64Array(3);
64
+ const _abc = new Float64Array(3);
65
+ const _acd = new Float64Array(3);
66
+ const _adb = new Float64Array(3);
67
+ const _t = new Float64Array(3);
68
+
69
+ // --- EPA polytope pools ---
70
+ const VERTS = new Float64Array(VERT_CAP * 3);
71
+ const FACE_IDX = new Int32Array(FACE_CAP * 3); // vertex indices, 3 per face
72
+ const FACE_N = new Float64Array(FACE_CAP * 3); // outward unit normal, 3 per face
73
+ const FACE_DIST = new Float64Array(FACE_CAP); // perpendicular distance from origin
74
+ const EDGE = new Int32Array(EDGE_CAP * 2); // horizon edges, 2 vertex indices each
75
+ const CEN = new Float64Array(3); // polytope centroid (for outward orientation)
45
76
 
46
77
  /**
47
- * Minkowski-difference support: support_A(d) support_B(−d), world space.
78
+ * A convex shape exposing a world-space support mapping: `support(out, off, dx,
79
+ * dy, dz)` writes the farthest point of the shape in direction `(dx, dy, dz)`
80
+ * into `out[off..off+2]` (e.g. {@link PosedShape}).
81
+ *
82
+ * @typedef {{ support: function(Float64Array, number, number, number, number): void }} SupportProvider
83
+ */
84
+
85
+ /**
86
+ * Minkowski-difference support written to `out[off..off+2]`:
87
+ * support_A(d̂) − support_B(−d̂), world space.
48
88
  *
49
89
  * The direction is normalised first: the support contract assumes a UNIT vector
50
90
  * — `SphereShape3D`/`CapsuleShape3D` scale the raw direction by their radius
@@ -53,188 +93,436 @@ function tripleProduct(a, b, c) {
53
93
  * simplex evolution). Polytope supports are scale-invariant (vertex argmax), so
54
94
  * this only matters for curved providers, but it must hold for them. Matches the
55
95
  * sibling `minkowski_support.js`, which normalises for the same reason.
96
+ *
97
+ * @param {Float64Array|number[]} out destination for the CSO point
98
+ * @param {number} off offset into `out`
99
+ * @param {SupportProvider} A first support provider
100
+ * @param {SupportProvider} B second support provider
101
+ * @param {number} dx search-direction x (need not be unit length)
102
+ * @param {number} dy search-direction y
103
+ * @param {number} dz search-direction z
104
+ * @returns {void}
56
105
  */
57
- function csoSupport(A, B, d) {
58
- let dx = d[0], dy = d[1], dz = d[2];
106
+ function cso_support(out, off, A, B, dx, dy, dz) {
59
107
  const len2 = dx * dx + dy * dy + dz * dz;
60
108
  if (len2 > 1e-30) {
61
109
  const inv = 1 / Math.sqrt(len2);
62
- dx *= inv; dy *= inv; dz *= inv;
110
+ dx *= inv;
111
+ dy *= inv;
112
+ dz *= inv;
63
113
  }
64
114
  A.support(_sA, 0, dx, dy, dz);
65
115
  B.support(_sB, 0, -dx, -dy, -dz);
66
- return [_sA[0] - _sB[0], _sA[1] - _sB[1], _sA[2] - _sB[2]];
116
+ out[off] = _sA[0] - _sB[0];
117
+ out[off + 1] = _sA[1] - _sB[1];
118
+ out[off + 2] = _sA[2] - _sB[2];
67
119
  }
68
120
 
69
121
  /**
70
- * GJK intersection. Returns a rank-4 simplex (array of 4 CSO points) enclosing
71
- * the origin on overlap, or null if separated. `simplex[0]` is the most-recent
72
- * point throughout (Muratori convention).
122
+ * GJK intersection. Fills {@link SIMPLEX} with a rank-4 simplex enclosing the
123
+ * origin and returns 4 on overlap; returns 0 if the shapes are separated.
124
+ * `SIMPLEX` slot 0 is the most-recent point throughout (Muratori convention).
125
+ *
126
+ * @param {SupportProvider} A first support provider
127
+ * @param {SupportProvider} B second support provider
128
+ * @returns {number} 4 if the origin is enclosed (shapes overlap), 0 if separated
73
129
  */
74
130
  function gjk(A, B) {
75
- let d = [1, 0, 0];
76
- let s = csoSupport(A, B, d);
77
- if (lensq(s) < 1e-18) { d = [0, 1, 0]; s = csoSupport(A, B, d); }
78
- const simplex = [s];
79
- d = neg(s);
131
+ cso_support(SIMPLEX, 0, A, B, 1, 0, 0);
132
+ if (v3_length_sqr(SIMPLEX[0], SIMPLEX[1], SIMPLEX[2]) < DIR_EPS) {
133
+ cso_support(SIMPLEX, 0, A, B, 0, 1, 0);
134
+ }
135
+ DIR[0] = -SIMPLEX[0];
136
+ DIR[1] = -SIMPLEX[1];
137
+ DIR[2] = -SIMPLEX[2];
138
+ let count = 1;
80
139
 
81
140
  for (let iter = 0; iter < GJK_MAX_ITER; iter++) {
82
- if (lensq(d) < 1e-18) return null; // search direction collapsed → touching/sep
83
- const a = csoSupport(A, B, d);
84
- if (dot(a, d) < 0) return null; // farthest point short of origin → separated
85
- simplex.unshift(a);
86
- if (doSimplex(simplex, d)) return simplex.slice(0, 4);
141
+ if (v3_length_sqr(DIR[0], DIR[1], DIR[2]) < DIR_EPS) {
142
+ return 0; // search direction collapsed → touching/separated
143
+ }
144
+ cso_support(_w, 0, A, B, DIR[0], DIR[1], DIR[2]);
145
+ if (v3_dot(_w[0], _w[1], _w[2], DIR[0], DIR[1], DIR[2]) < 0) {
146
+ return 0; // farthest point short of the origin → separated
147
+ }
148
+ // push the new point to slot 0, shifting the existing points down one slot
149
+ // (copyWithin is memmove-style: the overlapping forward copy is well-defined)
150
+ SIMPLEX.copyWithin(3, 0, count * 3);
151
+ SIMPLEX[0] = _w[0];
152
+ SIMPLEX[1] = _w[1];
153
+ SIMPLEX[2] = _w[2];
154
+ count++;
155
+
156
+ count = do_simplex(count);
157
+ if (count === 0) {
158
+ return 4; // origin enclosed by the tetrahedron
159
+ }
87
160
  }
88
- return null;
161
+ return 0;
89
162
  }
90
163
 
91
164
  /**
92
- * Evolve the simplex toward the origin. Mutates `simplex` (in place) and writes
93
- * the next search direction into `dOut`. Returns true when a tetrahedron
94
- * encloses the origin.
165
+ * Evolve the simplex toward the origin. Reorders {@link SIMPLEX} in place and
166
+ * writes the next search direction into {@link DIR}. Returns the new simplex size,
167
+ * or 0 when a tetrahedron encloses the origin.
168
+ *
169
+ * @param {number} count current simplex size (2, 3, or 4)
170
+ * @returns {number} the new simplex size (1–3), or 0 when the origin is enclosed
95
171
  */
96
- function doSimplex(simplex, dOut) {
97
- if (simplex.length === 2) return lineCase(simplex, dOut);
98
- if (simplex.length === 3) return triangleCase(simplex, dOut);
99
- return tetraCase(simplex, dOut);
172
+ function do_simplex(count) {
173
+ if (count === 2) {
174
+ return line_case();
175
+ }
176
+ if (count === 3) {
177
+ return triangle_case();
178
+ }
179
+ return tetra_case();
100
180
  }
101
181
 
102
- function setDir(dOut, v) { dOut[0] = v[0]; dOut[1] = v[1]; dOut[2] = v[2]; }
103
-
104
- function lineCase(simplex, dOut) {
105
- const a = simplex[0], b = simplex[1];
106
- const ab = sub(b, a), ao = neg(a);
107
- if (dot(ab, ao) > 0) {
108
- const perp = tripleProduct(ab, ao, ab);
109
- if (lensq(perp) < 1e-18) {
110
- // origin on the line pick any perpendicular
111
- const ref = Math.abs(ab[0]) < 0.9 ? [1, 0, 0] : [0, 1, 0];
112
- setDir(dOut, cross(ab, ref));
113
- } else setDir(dOut, perp);
114
- } else {
115
- simplex.length = 1; // keep [a]
116
- setDir(dOut, ao);
182
+ /**
183
+ * Line (2-point) simplex sub-case: pick the Voronoi region of segment [a, b] the
184
+ * origin projects into and set the next search direction in {@link DIR}.
185
+ *
186
+ * @returns {number} the new simplex size (1 keeps [a], 2 keeps [a, b])
187
+ */
188
+ function line_case() {
189
+ const ax = SIMPLEX[0], ay = SIMPLEX[1], az = SIMPLEX[2];
190
+ const bx = SIMPLEX[3], by = SIMPLEX[4], bz = SIMPLEX[5];
191
+ v3_subtract(_ab, 0, bx, by, bz, ax, ay, az); // ab = b − a
192
+ const aox = -ax, aoy = -ay, aoz = -az; // ao = −a
193
+
194
+ if (v3_dot(_ab[0], _ab[1], _ab[2], aox, aoy, aoz) > 0) {
195
+ v3_triple_cross_product(DIR, 0, _ab[0], _ab[1], _ab[2], aox, aoy, aoz); // (ab×ao)×ab
196
+ if (v3_length_sqr(DIR[0], DIR[1], DIR[2]) < DIR_EPS) {
197
+ // origin on the line — pick any perpendicular to ab
198
+ if (Math.abs(_ab[0]) < 0.9) {
199
+ v3_cross(DIR, 0, _ab[0], _ab[1], _ab[2], 1, 0, 0);
200
+ } else {
201
+ v3_cross(DIR, 0, _ab[0], _ab[1], _ab[2], 0, 1, 0);
202
+ }
203
+ }
204
+ return 2; // keep [a, b]
117
205
  }
118
- return false;
206
+ DIR[0] = aox;
207
+ DIR[1] = aoy;
208
+ DIR[2] = aoz;
209
+ return 1; // keep [a]
119
210
  }
120
211
 
121
- function triangleCase(simplex, dOut) {
122
- const a = simplex[0], b = simplex[1], c = simplex[2];
123
- const ab = sub(b, a), ac = sub(c, a), ao = neg(a);
124
- const abc = cross(ab, ac);
212
+ /**
213
+ * Triangle (3-point) simplex sub-case: classify the origin against triangle
214
+ * [a, b, c]'s edges and faces, reorder {@link SIMPLEX}, and set the next search
215
+ * direction in {@link DIR}.
216
+ *
217
+ * @returns {number} the new simplex size (1, 2, or 3)
218
+ */
219
+ function triangle_case() {
220
+ const ax = SIMPLEX[0], ay = SIMPLEX[1], az = SIMPLEX[2];
221
+ const bx = SIMPLEX[3], by = SIMPLEX[4], bz = SIMPLEX[5];
222
+ const cx = SIMPLEX[6], cy = SIMPLEX[7], cz = SIMPLEX[8];
223
+ v3_subtract(_ab, 0, bx, by, bz, ax, ay, az);
224
+ v3_subtract(_ac, 0, cx, cy, cz, ax, ay, az);
225
+ const aox = -ax, aoy = -ay, aoz = -az;
226
+ v3_cross(_abc, 0, _ab[0], _ab[1], _ab[2], _ac[0], _ac[1], _ac[2]); // abc = ab×ac
125
227
 
126
- if (dot(cross(abc, ac), ao) > 0) {
127
- if (dot(ac, ao) > 0) { // outside edge ac
128
- simplex.length = 0; simplex.push(a, c);
129
- setDir(dOut, tripleProduct(ac, ao, ac));
130
- return false;
228
+ // outside edge ac? (abc×ac) · ao > 0
229
+ v3_cross(_t, 0, _abc[0], _abc[1], _abc[2], _ac[0], _ac[1], _ac[2]);
230
+ if (v3_dot(_t[0], _t[1], _t[2], aox, aoy, aoz) > 0) {
231
+ if (v3_dot(_ac[0], _ac[1], _ac[2], aox, aoy, aoz) > 0) {
232
+ // keep [a, c]: move c into slot 1
233
+ v3_array_copy(SIMPLEX, 3, SIMPLEX, 6);
234
+ v3_triple_cross_product(DIR, 0, _ac[0], _ac[1], _ac[2], aox, aoy, aoz); // (ac×ao)×ac
235
+ return 2;
131
236
  }
132
- return starCase(simplex, a, b, ao, dOut);
237
+ return star_case();
133
238
  }
134
- if (dot(cross(ab, abc), ao) > 0) return starCase(simplex, a, b, ao, dOut);
135
239
 
136
- // origin within the triangle's prism above or below
137
- if (dot(abc, ao) > 0) { simplex.length = 0; simplex.push(a, b, c); setDir(dOut, abc); }
138
- else { simplex.length = 0; simplex.push(a, c, b); setDir(dOut, neg(abc)); }
139
- return false;
140
- }
240
+ // outside edge ab? (ab×abc) · ao > 0
241
+ v3_cross(_t, 0, _ab[0], _ab[1], _ab[2], _abc[0], _abc[1], _abc[2]);
242
+ if (v3_dot(_t[0], _t[1], _t[2], aox, aoy, aoz) > 0) {
243
+ return star_case();
244
+ }
141
245
 
142
- function starCase(simplex, a, b, ao, dOut) {
143
- const ab = sub(b, a);
144
- if (dot(ab, ao) > 0) {
145
- simplex.length = 0; simplex.push(a, b);
146
- setDir(dOut, tripleProduct(ab, ao, ab));
246
+ // origin within the triangle's prism — above or below the face
247
+ if (v3_dot(_abc[0], _abc[1], _abc[2], aox, aoy, aoz) > 0) {
248
+ DIR[0] = _abc[0];
249
+ DIR[1] = _abc[1];
250
+ DIR[2] = _abc[2];
147
251
  } else {
148
- simplex.length = 0; simplex.push(a);
149
- setDir(dOut, ao);
252
+ // below: reorder to [a, c, b] so the winding faces the origin; DIR = −abc
253
+ SIMPLEX[3] = cx;
254
+ SIMPLEX[4] = cy;
255
+ SIMPLEX[5] = cz;
256
+ SIMPLEX[6] = bx;
257
+ SIMPLEX[7] = by;
258
+ SIMPLEX[8] = bz;
259
+ DIR[0] = -_abc[0];
260
+ DIR[1] = -_abc[1];
261
+ DIR[2] = -_abc[2];
150
262
  }
151
- return false;
263
+ return 3; // keep the triangle
152
264
  }
153
265
 
154
- function tetraCase(simplex, dOut) {
155
- const a = simplex[0], b = simplex[1], c = simplex[2], d = simplex[3];
156
- const ab = sub(b, a), ac = sub(c, a), ad = sub(d, a), ao = neg(a);
157
- const abc = cross(ab, ac);
158
- const acd = cross(ac, ad);
159
- const adb = cross(ad, ab);
160
-
161
- if (dot(abc, ao) > 0) { simplex.length = 0; simplex.push(a, b, c); return triangleCase(simplex, dOut); }
162
- if (dot(acd, ao) > 0) { simplex.length = 0; simplex.push(a, c, d); return triangleCase(simplex, dOut); }
163
- if (dot(adb, ao) > 0) { simplex.length = 0; simplex.push(a, d, b); return triangleCase(simplex, dOut); }
164
- return true; // origin inside the tetrahedron
266
+ /**
267
+ * Edge region shared by the two triangle edges that fold back toward [a]: keep
268
+ * segment [a, b] or vertex [a] and set the next search direction in {@link DIR}.
269
+ *
270
+ * @returns {number} the new simplex size (1 keeps [a], 2 keeps [a, b])
271
+ */
272
+ function star_case() {
273
+ const ax = SIMPLEX[0], ay = SIMPLEX[1], az = SIMPLEX[2];
274
+ const bx = SIMPLEX[3], by = SIMPLEX[4], bz = SIMPLEX[5];
275
+ v3_subtract(_ab, 0, bx, by, bz, ax, ay, az);
276
+ const aox = -ax, aoy = -ay, aoz = -az;
277
+
278
+ if (v3_dot(_ab[0], _ab[1], _ab[2], aox, aoy, aoz) > 0) {
279
+ v3_triple_cross_product(DIR, 0, _ab[0], _ab[1], _ab[2], aox, aoy, aoz);
280
+ return 2; // keep [a, b]
281
+ }
282
+ DIR[0] = aox;
283
+ DIR[1] = aoy;
284
+ DIR[2] = aoz;
285
+ return 1; // keep [a]
165
286
  }
166
287
 
167
288
  /**
168
- * EPA over the Minkowski difference, seeded by a rank-4 enclosing simplex.
169
- * Writes the unit penetration normal (B→A) into `out` and returns the depth.
289
+ * Tetrahedron (4-point) simplex sub-case: if the origin lies outside one of the
290
+ * three faces incident to [a], reorder {@link SIMPLEX} to that triangle and
291
+ * recurse into {@link triangle_case}; otherwise the origin is enclosed.
292
+ *
293
+ * @returns {number} the new simplex size (1–3), or 0 when the origin is enclosed
170
294
  */
171
- function epa(A, B, simplex, out) {
172
- const verts = simplex.map((p) => [p[0], p[1], p[2]]);
173
- // centroid for outward orientation of the initial faces
174
- const cen = [(verts[0][0] + verts[1][0] + verts[2][0] + verts[3][0]) / 4,
175
- (verts[0][1] + verts[1][1] + verts[2][1] + verts[3][1]) / 4,
176
- (verts[0][2] + verts[1][2] + verts[2][2] + verts[3][2]) / 4];
295
+ function tetra_case() {
296
+ const ax = SIMPLEX[0], ay = SIMPLEX[1], az = SIMPLEX[2];
297
+ const bx = SIMPLEX[3], by = SIMPLEX[4], bz = SIMPLEX[5];
298
+ const cx = SIMPLEX[6], cy = SIMPLEX[7], cz = SIMPLEX[8];
299
+ const dx = SIMPLEX[9], dy = SIMPLEX[10], dz = SIMPLEX[11];
300
+ v3_subtract(_ab, 0, bx, by, bz, ax, ay, az);
301
+ v3_subtract(_ac, 0, cx, cy, cz, ax, ay, az);
302
+ v3_subtract(_ad, 0, dx, dy, dz, ax, ay, az);
303
+ const aox = -ax, aoy = -ay, aoz = -az;
304
+ v3_cross(_abc, 0, _ab[0], _ab[1], _ab[2], _ac[0], _ac[1], _ac[2]);
305
+ v3_cross(_acd, 0, _ac[0], _ac[1], _ac[2], _ad[0], _ad[1], _ad[2]);
306
+ v3_cross(_adb, 0, _ad[0], _ad[1], _ad[2], _ab[0], _ab[1], _ab[2]);
177
307
 
178
- let faces = [[0, 1, 2], [0, 2, 3], [0, 3, 1], [1, 3, 2]].map((f) => makeFace(verts, f, cen));
308
+ if (v3_dot(_abc[0], _abc[1], _abc[2], aox, aoy, aoz) > 0) {
309
+ // outside face abc → keep [a, b, c] (slots already correct)
310
+ return triangle_case();
311
+ }
312
+ if (v3_dot(_acd[0], _acd[1], _acd[2], aox, aoy, aoz) > 0) {
313
+ // outside face acd → reorder to [a, c, d]
314
+ SIMPLEX[3] = cx;
315
+ SIMPLEX[4] = cy;
316
+ SIMPLEX[5] = cz;
317
+ SIMPLEX[6] = dx;
318
+ SIMPLEX[7] = dy;
319
+ SIMPLEX[8] = dz;
320
+ return triangle_case();
321
+ }
322
+ if (v3_dot(_adb[0], _adb[1], _adb[2], aox, aoy, aoz) > 0) {
323
+ // outside face adb → reorder to [a, d, b]
324
+ SIMPLEX[3] = dx;
325
+ SIMPLEX[4] = dy;
326
+ SIMPLEX[5] = dz;
327
+ SIMPLEX[6] = bx;
328
+ SIMPLEX[7] = by;
329
+ SIMPLEX[8] = bz;
330
+ return triangle_case();
331
+ }
332
+ return 0; // origin inside the tetrahedron
333
+ }
334
+
335
+ /**
336
+ * Append a face spanning vertices (i, j, k) with an OUTWARD unit normal (away
337
+ * from the polytope centroid {@link CEN}); winding is flipped if needed to keep
338
+ * the normal outward.
339
+ *
340
+ * @param {number} i first vertex index into {@link VERTS}
341
+ * @param {number} j second vertex index into {@link VERTS}
342
+ * @param {number} k third vertex index into {@link VERTS}
343
+ * @param {number} face_count current number of faces (the new face's slot)
344
+ * @returns {number} the new face count
345
+ */
346
+ function add_face(i, j, k, face_count) {
347
+ const ax = VERTS[i * 3], ay = VERTS[i * 3 + 1], az = VERTS[i * 3 + 2];
348
+ const bx = VERTS[j * 3], by = VERTS[j * 3 + 1], bz = VERTS[j * 3 + 2];
349
+ const cx = VERTS[k * 3], cy = VERTS[k * 3 + 1], cz = VERTS[k * 3 + 2];
350
+ // (b−a)×(c−a), normalised (degenerate → (0,1,0)); see v3_compute_triangle_normal
351
+ v3_compute_triangle_normal(_t, 0, ax, ay, az, bx, by, bz, cx, cy, cz);
352
+ let nx = _t[0], ny = _t[1], nz = _t[2];
353
+
354
+ // orient outward: the normal must point away from the interior (centroid)
355
+ if (v3_dot(nx, ny, nz, ax - CEN[0], ay - CEN[1], az - CEN[2]) < 0) {
356
+ nx = -nx;
357
+ ny = -ny;
358
+ nz = -nz;
359
+ const swap = j;
360
+ j = k;
361
+ k = swap;
362
+ }
363
+
364
+ const fb = face_count * 3;
365
+ FACE_IDX[fb] = i;
366
+ FACE_IDX[fb + 1] = j;
367
+ FACE_IDX[fb + 2] = k;
368
+ FACE_N[fb] = nx;
369
+ FACE_N[fb + 1] = ny;
370
+ FACE_N[fb + 2] = nz;
371
+ FACE_DIST[face_count] = v3_dot(nx, ny, nz, ax, ay, az); // ≥ 0 once outward
372
+ return face_count + 1;
373
+ }
374
+
375
+ /**
376
+ * Add a horizon edge (i, j) with cancellation: if the reverse edge (j, i) is
377
+ * already present, both are interior to the visible region and drop out.
378
+ * Order is irrelevant to the horizon, so removal is an O(1) swap-with-last.
379
+ *
380
+ * @param {number} edge_count current number of horizon edges in {@link EDGE}
381
+ * @param {number} i edge start (vertex index)
382
+ * @param {number} j edge end (vertex index)
383
+ * @returns {number} the new edge count
384
+ */
385
+ function add_edge(edge_count, i, j) {
386
+ for (let k = 0; k < edge_count; k++) {
387
+ if (EDGE[k * 2] === j && EDGE[k * 2 + 1] === i) {
388
+ const last = edge_count - 1;
389
+ EDGE[k * 2] = EDGE[last * 2];
390
+ EDGE[k * 2 + 1] = EDGE[last * 2 + 1];
391
+ return last;
392
+ }
393
+ }
394
+ if (edge_count >= EDGE_CAP) {
395
+ return edge_count; // horizon pool full (unreachable for convex inputs)
396
+ }
397
+ EDGE[edge_count * 2] = i;
398
+ EDGE[edge_count * 2 + 1] = j;
399
+ return edge_count + 1;
400
+ }
401
+
402
+ /**
403
+ * Copy a face record (vertex indices, normal, distance) from slot `src` to slot
404
+ * `dst` — used to compact surviving faces over removed ones.
405
+ *
406
+ * @param {number} dst destination face slot
407
+ * @param {number} src source face slot
408
+ * @returns {void}
409
+ */
410
+ function copy_face(dst, src) {
411
+ const db = dst * 3, sb = src * 3;
412
+ FACE_IDX[db] = FACE_IDX[sb];
413
+ FACE_IDX[db + 1] = FACE_IDX[sb + 1];
414
+ FACE_IDX[db + 2] = FACE_IDX[sb + 2];
415
+ FACE_N[db] = FACE_N[sb];
416
+ FACE_N[db + 1] = FACE_N[sb + 1];
417
+ FACE_N[db + 2] = FACE_N[sb + 2];
418
+ FACE_DIST[dst] = FACE_DIST[src];
419
+ }
420
+
421
+ /**
422
+ * EPA over the Minkowski difference, seeded by the rank-4 enclosing simplex left
423
+ * in {@link SIMPLEX} by {@link gjk}. Writes the unit penetration normal (B→A)
424
+ * into `out` and returns the depth.
425
+ *
426
+ * @param {SupportProvider} A first support provider
427
+ * @param {SupportProvider} B second support provider
428
+ * @param {Float64Array|number[]} out destination for the unit penetration normal (B→A)
429
+ * @returns {number} penetration depth, or 0 if the polytope degenerated
430
+ */
431
+ function epa(A, B, out) {
432
+ // seed the vertex pool with the simplex points
433
+ v3_array_copy(VERTS, 0, SIMPLEX, 0);
434
+ v3_array_copy(VERTS, 3, SIMPLEX, 3);
435
+ v3_array_copy(VERTS, 6, SIMPLEX, 6);
436
+ v3_array_copy(VERTS, 9, SIMPLEX, 9);
437
+ let vert_count = 4;
438
+
439
+ CEN[0] = (VERTS[0] + VERTS[3] + VERTS[6] + VERTS[9]) / 4;
440
+ CEN[1] = (VERTS[1] + VERTS[4] + VERTS[7] + VERTS[10]) / 4;
441
+ CEN[2] = (VERTS[2] + VERTS[5] + VERTS[8] + VERTS[11]) / 4;
442
+
443
+ let face_count = 0;
444
+ face_count = add_face(0, 1, 2, face_count);
445
+ face_count = add_face(0, 2, 3, face_count);
446
+ face_count = add_face(0, 3, 1, face_count);
447
+ face_count = add_face(1, 3, 2, face_count);
179
448
 
180
449
  for (let iter = 0; iter < EPA_MAX_ITER; iter++) {
181
450
  // closest face to the origin
182
- let best = faces[0];
183
- for (let i = 1; i < faces.length; i++) if (faces[i].dist < best.dist) best = faces[i];
184
-
185
- const w = csoSupport(A, B, best.n);
186
- const wdist = dot(best.n, w) - best.dist;
187
- if (wdist < EPA_EPS) break; // converged onto the closest face
188
-
189
- const wi = verts.length;
190
- verts.push(w);
191
-
192
- // remove faces visible from w; collect horizon edges (those on exactly one removed face)
193
- const edges = [];
194
- const kept = [];
195
- for (const f of faces) {
196
- if (dot(f.n, w) - f.dist > EPA_EPS) {
197
- addEdge(edges, f.v[0], f.v[1]);
198
- addEdge(edges, f.v[1], f.v[2]);
199
- addEdge(edges, f.v[2], f.v[0]);
200
- } else kept.push(f);
451
+ let best = 0;
452
+ for (let f = 1; f < face_count; f++) {
453
+ if (FACE_DIST[f] < FACE_DIST[best]) {
454
+ best = f;
455
+ }
456
+ }
457
+
458
+ const bn = best * 3;
459
+ cso_support(_w, 0, A, B, FACE_N[bn], FACE_N[bn + 1], FACE_N[bn + 2]);
460
+ const wdist = v3_dot(FACE_N[bn], FACE_N[bn + 1], FACE_N[bn + 2], _w[0], _w[1], _w[2]) - FACE_DIST[best];
461
+ if (wdist < EPA_EPS) {
462
+ break; // converged onto the closest face
463
+ }
464
+ if (vert_count >= VERT_CAP) {
465
+ break; // vertex pool full (unreachable: EPA_MAX_ITER caps vertices at 68)
466
+ }
467
+
468
+ const wi = vert_count;
469
+ VERTS[wi * 3] = _w[0];
470
+ VERTS[wi * 3 + 1] = _w[1];
471
+ VERTS[wi * 3 + 2] = _w[2];
472
+ vert_count++;
473
+
474
+ // remove faces visible from w, compacting the survivors; collect the
475
+ // horizon (edges on exactly one removed face) with cancellation
476
+ let edge_count = 0;
477
+ let kept = 0;
478
+ for (let f = 0; f < face_count; f++) {
479
+ const fn = f * 3;
480
+ if (v3_dot(FACE_N[fn], FACE_N[fn + 1], FACE_N[fn + 2], _w[0], _w[1], _w[2]) - FACE_DIST[f] > EPA_EPS) {
481
+ const i0 = FACE_IDX[fn], i1 = FACE_IDX[fn + 1], i2 = FACE_IDX[fn + 2];
482
+ edge_count = add_edge(edge_count, i0, i1);
483
+ edge_count = add_edge(edge_count, i1, i2);
484
+ edge_count = add_edge(edge_count, i2, i0);
485
+ } else {
486
+ if (kept !== f) {
487
+ copy_face(kept, f);
488
+ }
489
+ kept++;
490
+ }
491
+ }
492
+ face_count = kept;
493
+
494
+ for (let e = 0; e < edge_count; e++) {
495
+ if (face_count >= FACE_CAP) {
496
+ break; // face pool full (unreachable for convex inputs)
497
+ }
498
+ face_count = add_face(EDGE[e * 2], EDGE[e * 2 + 1], wi, face_count);
499
+ }
500
+ if (face_count === 0) {
501
+ break; // degenerate polytope (all faces visible + horizon cancelled)
201
502
  }
202
- faces = kept;
203
- for (const [i, j] of edges) faces.push(makeFace(verts, [i, j, wi], cen));
204
- if (faces.length === 0) break; // degenerate polytope (all faces visible + horizon cancelled)
205
503
  }
206
504
 
207
- // recompute the closest face (faces may have changed on the last iteration)
208
- if (faces.length === 0) {
209
- // Degenerate: no face survived. Report no usable axis (caller treats a
210
- // non-positive depth as a miss) rather than dereferencing faces[0] and
211
- // throwing a TypeError out of the narrowphase step.
212
- out[0] = 0; out[1] = 0; out[2] = 0;
505
+ if (face_count === 0) {
506
+ // Degenerate: no face survived. Report no usable axis (the caller treats a
507
+ // non-positive depth as a miss) rather than reading a stale face and
508
+ // throwing out of the narrowphase step.
509
+ out[0] = 0;
510
+ out[1] = 0;
511
+ out[2] = 0;
213
512
  return 0;
214
513
  }
215
- let best = faces[0];
216
- for (let i = 1; i < faces.length; i++) if (faces[i].dist < best.dist) best = faces[i];
217
- out[0] = best.n[0]; out[1] = best.n[1]; out[2] = best.n[2];
218
- return best.dist;
219
- }
220
514
 
221
- /** Build a face with an OUTWARD unit normal (away from the polytope centroid). */
222
- function makeFace(verts, idx, cen) {
223
- const a = verts[idx[0]], b = verts[idx[1]], c = verts[idx[2]];
224
- let n = cross(sub(b, a), sub(c, a));
225
- const l = Math.sqrt(lensq(n)) || 1;
226
- n = [n[0] / l, n[1] / l, n[2] / l];
227
- // orient outward: normal should point away from the polytope interior (centroid)
228
- if (dot(n, sub(a, cen)) < 0) { n = neg(n); const t = idx[1]; idx = [idx[0], idx[2], t]; }
229
- return { v: idx, n, dist: dot(n, a) }; // perpendicular distance from origin (≥0 once outward)
230
- }
231
-
232
- /** Add an edge with horizon cancellation: if the reverse edge is present, drop both. */
233
- function addEdge(edges, i, j) {
234
- for (let k = 0; k < edges.length; k++) {
235
- if (edges[k][0] === j && edges[k][1] === i) { edges.splice(k, 1); return; }
515
+ let best = 0;
516
+ for (let f = 1; f < face_count; f++) {
517
+ if (FACE_DIST[f] < FACE_DIST[best]) {
518
+ best = f;
519
+ }
236
520
  }
237
- edges.push([i, j]);
521
+ const bn = best * 3;
522
+ out[0] = FACE_N[bn];
523
+ out[1] = FACE_N[bn + 1];
524
+ out[2] = FACE_N[bn + 2];
525
+ return FACE_DIST[best];
238
526
  }
239
527
 
240
528
  /**
@@ -242,14 +530,15 @@ function addEdge(edges, i, j) {
242
530
  * Writes the unit separation normal (B→A) into `out` and returns the depth
243
531
  * (positive on overlap, 0 if separated — `out` untouched on 0).
244
532
  *
245
- * @param {Float64Array|number[]} out length ≥ 3
246
- * @param {{support:Function}} A world-space support provider (e.g. PosedShape)
247
- * @param {{support:Function}} B
533
+ * @param {Float64Array|number[]} out destination for the unit normal, length ≥ 3
534
+ * @param {SupportProvider} A world-space support provider (e.g. PosedShape)
535
+ * @param {SupportProvider} B world-space support provider (e.g. PosedShape)
248
536
  * @returns {number} penetration depth, or 0 if separated
249
537
  */
250
538
  export function gjk_epa_penetration(out, A, B) {
251
- const simplex = gjk(A, B);
252
- if (simplex === null || simplex.length < 4) return 0;
253
- const depth = epa(A, B, simplex, out);
539
+ if (gjk(A, B) < 4) {
540
+ return 0;
541
+ }
542
+ const depth = epa(A, B, out);
254
543
  return depth > 0 && Number.isFinite(depth) ? depth : 0;
255
544
  }