@woosh/meep-engine 2.169.1 → 2.169.2

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 (680) hide show
  1. package/editor/ecs/component/editors/NumericIntervalEditor.js +18 -35
  2. package/editor/ecs/component/editors/ObservedIntegerEditor.js +22 -43
  3. package/editor/ecs/component/editors/ObservedStringEditor.js +34 -51
  4. package/editor/ecs/component/editors/common/makeV3_editor.js +12 -30
  5. package/editor/ecs/component/editors/common/two_way_sync.js +50 -0
  6. package/editor/ecs/component/editors/geom/QuaternionEditor.js +20 -35
  7. package/editor/ecs/component/editors/geom/Vector1Editor.js +5 -28
  8. package/editor/ecs/component/editors/primitive/NumberEditor.js +15 -31
  9. package/editor/tools/paint/TerrainHeightPaintTool.js +22 -31
  10. package/editor/tools/paint/TerrainPaintTool.js +85 -13
  11. package/editor/tools/paint/TerrainTexturePaintTool.js +18 -29
  12. package/editor/view/ecs/components/TerrainController.js +1 -4
  13. package/package.json +1 -1
  14. package/src/DUPLICATION_AUDIT_2026_08_07/00_core_reuse_surface.md +738 -0
  15. package/src/DUPLICATION_AUDIT_2026_08_07/A_core_math_geom.md +79 -0
  16. package/src/DUPLICATION_AUDIT_2026_08_07/B_core_data.md +93 -0
  17. package/src/DUPLICATION_AUDIT_2026_08_07/C_gfx_render.md +80 -0
  18. package/src/DUPLICATION_AUDIT_2026_08_07/D_gfx_features.md +164 -0
  19. package/src/DUPLICATION_AUDIT_2026_08_07/E_physics.md +69 -0
  20. package/src/DUPLICATION_AUDIT_2026_08_07/F_sound.md +102 -0
  21. package/src/DUPLICATION_AUDIT_2026_08_07/G_net_save.md +129 -0
  22. package/src/DUPLICATION_AUDIT_2026_08_07/H_engine_misc.md +72 -0
  23. package/src/DUPLICATION_AUDIT_2026_08_07/I_generation_view.md +74 -0
  24. package/src/DUPLICATION_AUDIT_2026_08_07/K_crosscutting.md +169 -0
  25. package/src/DUPLICATION_AUDIT_2026_08_07.md +254 -0
  26. package/src/DUPLICATION_AUDIT_REMEDIATION.md +250 -0
  27. package/src/core/bvh2/bvh3/query/bvh_collect_user_data.js +2 -2
  28. package/src/core/bvh2/bvh3/query/bvh_query_leaves_generic.js +77 -77
  29. package/src/core/bvh2/bvh3/query/bvh_query_leaves_ray.js +90 -90
  30. package/src/core/bvh2/bvh3/query/bvh_query_leaves_ray_segment.js +94 -94
  31. package/src/core/bvh2/bvh3/query/bvh_query_user_data_generic.js +86 -86
  32. package/src/core/bvh2/bvh3/query/bvh_query_user_data_intersects_aabb.js +2 -2
  33. package/src/core/bvh2/bvh3/query/bvh_query_user_data_intersects_capsule.js +2 -2
  34. package/src/core/bvh2/bvh3/query/bvh_query_user_data_intersects_frustum.js +2 -2
  35. package/src/core/bvh2/bvh3/query/bvh_query_user_data_intersects_sphere.js +2 -2
  36. package/src/core/bvh2/bvh3/query/bvh_query_user_data_ray.js +96 -96
  37. package/src/core/bvh2/bvh3/query/bvh_query_user_data_ray_segment.js +100 -100
  38. package/src/core/bvh2/bvh3/query/compute_tight_near_far_clipping_planes.js +2 -2
  39. package/src/core/bvh8/aabb3_round_to_float32.d.ts +16 -0
  40. package/src/core/bvh8/aabb3_round_to_float32.d.ts.map +1 -0
  41. package/src/core/bvh8/aabb3_round_to_float32.js +22 -0
  42. package/src/core/bvh8/bvh8_geometry_validate.d.ts.map +1 -1
  43. package/src/core/bvh8/bvh8_geometry_validate.js +1 -12
  44. package/src/core/bvh8/bvh8_geometry_validate_indirect.d.ts.map +1 -1
  45. package/src/core/bvh8/bvh8_geometry_validate_indirect.js +1 -12
  46. package/src/core/cache/Cache.d.ts +13 -0
  47. package/src/core/cache/Cache.d.ts.map +1 -1
  48. package/src/core/cache/Cache.js +13 -0
  49. package/src/core/color/operations/color_darken.d.ts +5 -2
  50. package/src/core/color/operations/color_darken.d.ts.map +1 -1
  51. package/src/core/color/operations/color_darken.js +24 -33
  52. package/src/core/color/operations/color_desaturate.d.ts +5 -2
  53. package/src/core/color/operations/color_desaturate.d.ts.map +1 -1
  54. package/src/core/color/operations/color_desaturate.js +24 -33
  55. package/src/core/color/operations/color_lighten.d.ts +5 -2
  56. package/src/core/color/operations/color_lighten.d.ts.map +1 -1
  57. package/src/core/color/operations/color_lighten.js +24 -33
  58. package/src/core/color/operations/color_saturate.d.ts +5 -2
  59. package/src/core/color/operations/color_saturate.d.ts.map +1 -1
  60. package/src/core/color/operations/color_saturate.js +24 -33
  61. package/src/core/color/operations/color_scale_okhsv_channel.d.ts +28 -0
  62. package/src/core/color/operations/color_scale_okhsv_channel.d.ts.map +1 -0
  63. package/src/core/color/operations/color_scale_okhsv_channel.js +45 -0
  64. package/src/core/geom/2d/hash-grid/SpatialHashGrid.d.ts.map +1 -1
  65. package/src/core/geom/2d/hash-grid/SpatialHashGrid.js +12 -0
  66. package/src/core/geom/2d/line/segment2_intersects_segment2_2d.d.ts.map +1 -1
  67. package/src/core/geom/2d/line/segment2_intersects_segment2_2d.js +25 -29
  68. package/src/core/geom/2d/line/segment2_segment_compute_intersection_array_2d.d.ts.map +1 -1
  69. package/src/core/geom/2d/line/segment2_segment_compute_intersection_array_2d.js +46 -51
  70. package/src/core/geom/2d/line/segment2_segment_intersection_fractions.d.ts +31 -0
  71. package/src/core/geom/2d/line/segment2_segment_intersection_fractions.d.ts.map +1 -0
  72. package/src/core/geom/2d/line/segment2_segment_intersection_fractions.js +52 -0
  73. package/src/core/geom/2d/quad-tree/qt_collect_by_circle.d.ts +6 -1
  74. package/src/core/geom/2d/quad-tree/qt_collect_by_circle.d.ts.map +1 -1
  75. package/src/core/geom/2d/quad-tree/qt_collect_by_circle.js +39 -65
  76. package/src/core/geom/2d/quad-tree/qt_match_data_by_circle.d.ts +15 -5
  77. package/src/core/geom/2d/quad-tree/qt_match_data_by_circle.d.ts.map +1 -1
  78. package/src/core/geom/2d/quad-tree/qt_match_data_by_circle.js +101 -70
  79. package/src/core/geom/2d/r-tree/StaticR2Tree.d.ts.map +1 -1
  80. package/src/core/geom/2d/r-tree/StaticR2Tree.js +7 -3
  81. package/src/core/geom/3d/aabb/aabb3_intersects_ray_segment.d.ts +7 -8
  82. package/src/core/geom/3d/aabb/aabb3_intersects_ray_segment.d.ts.map +1 -1
  83. package/src/core/geom/3d/aabb/aabb3_intersects_ray_segment.js +15 -51
  84. package/src/core/geom/3d/atlas/param/atlas_chart_parameterize_bff.d.ts.map +1 -1
  85. package/src/core/geom/3d/atlas/param/atlas_chart_parameterize_bff.js +13 -14
  86. package/src/core/geom/3d/atlas/param/atlas_chart_parameterize_lscm.d.ts.map +1 -1
  87. package/src/core/geom/3d/atlas/param/atlas_chart_parameterize_lscm.js +2 -17
  88. package/src/core/geom/3d/atlas/segment/atlas_chart_roundness_delta.d.ts +22 -0
  89. package/src/core/geom/3d/atlas/segment/atlas_chart_roundness_delta.d.ts.map +1 -0
  90. package/src/core/geom/3d/atlas/segment/atlas_chart_roundness_delta.js +30 -0
  91. package/src/core/geom/3d/atlas/segment/atlas_segment_charts.d.ts +19 -0
  92. package/src/core/geom/3d/atlas/segment/atlas_segment_charts.d.ts.map +1 -1
  93. package/src/core/geom/3d/atlas/segment/atlas_segment_charts.js +5 -9
  94. package/src/core/geom/3d/atlas/segment/atlas_segment_charts_atomic.d.ts.map +1 -1
  95. package/src/core/geom/3d/atlas/segment/atlas_segment_charts_atomic.js +4 -12
  96. package/src/core/geom/3d/frustum/read_frustum_corner.d.ts +5 -0
  97. package/src/core/geom/3d/frustum/read_frustum_corner.d.ts.map +1 -1
  98. package/src/core/geom/3d/frustum/read_frustum_corner.js +8 -5
  99. package/src/core/geom/3d/line/line3_segment_segment_closest_point_pair.d.ts +33 -0
  100. package/src/core/geom/3d/line/line3_segment_segment_closest_point_pair.d.ts.map +1 -0
  101. package/src/core/geom/3d/line/line3_segment_segment_closest_point_pair.js +71 -0
  102. package/src/core/geom/3d/mat4/m4_compose.d.ts +5 -0
  103. package/src/core/geom/3d/mat4/m4_compose.d.ts.map +1 -1
  104. package/src/core/geom/3d/mat4/m4_compose.js +22 -53
  105. package/src/core/geom/3d/mat4/m4_decompose.d.ts +8 -0
  106. package/src/core/geom/3d/mat4/m4_decompose.d.ts.map +1 -1
  107. package/src/core/geom/3d/mat4/m4_decompose.js +84 -76
  108. package/src/core/geom/3d/mat4/m4_from_rotation_translation_scale.d.ts +2 -0
  109. package/src/core/geom/3d/mat4/m4_from_rotation_translation_scale.d.ts.map +1 -1
  110. package/src/core/geom/3d/mat4/m4_from_rotation_translation_scale.js +10 -44
  111. package/src/core/geom/3d/mat4/m4_from_rotation_translation_scale_scalar.d.ts +27 -0
  112. package/src/core/geom/3d/mat4/m4_from_rotation_translation_scale_scalar.d.ts.map +1 -0
  113. package/src/core/geom/3d/mat4/m4_from_rotation_translation_scale_scalar.js +67 -0
  114. package/src/core/geom/3d/plane/orient3d_fast.d.ts.map +1 -1
  115. package/src/core/geom/3d/plane/orient3d_fast.js +2 -22
  116. package/src/core/geom/3d/plane/orient3d_fast_point.d.ts +28 -0
  117. package/src/core/geom/3d/plane/orient3d_fast_point.d.ts.map +1 -0
  118. package/src/core/geom/3d/plane/orient3d_fast_point.js +47 -0
  119. package/src/core/geom/3d/quaternion/quat3_nlerp.d.ts +48 -0
  120. package/src/core/geom/3d/quaternion/quat3_nlerp.d.ts.map +1 -0
  121. package/src/core/geom/3d/quaternion/quat3_nlerp.js +72 -0
  122. package/src/core/geom/3d/shape/AbstractShape3D.d.ts +9 -0
  123. package/src/core/geom/3d/shape/AbstractShape3D.d.ts.map +1 -1
  124. package/src/core/geom/3d/shape/AbstractShape3D.js +44 -1
  125. package/src/core/geom/3d/shape/BoxShape3D.d.ts.map +1 -1
  126. package/src/core/geom/3d/shape/BoxShape3D.js +2 -1
  127. package/src/core/geom/3d/shape/CapsuleShape3D.d.ts.map +1 -1
  128. package/src/core/geom/3d/shape/CapsuleShape3D.js +2 -1
  129. package/src/core/geom/3d/shape/ConvexHullShape3D.d.ts +0 -1
  130. package/src/core/geom/3d/shape/ConvexHullShape3D.d.ts.map +1 -1
  131. package/src/core/geom/3d/shape/ConvexHullShape3D.js +25 -56
  132. package/src/core/geom/3d/shape/CylinderShape3D.d.ts.map +1 -1
  133. package/src/core/geom/3d/shape/CylinderShape3D.js +2 -1
  134. package/src/core/geom/3d/shape/HeightMapShape3D.d.ts.map +1 -1
  135. package/src/core/geom/3d/shape/HeightMapShape3D.js +3 -5
  136. package/src/core/geom/3d/shape/MeshShape3D.d.ts +0 -11
  137. package/src/core/geom/3d/shape/MeshShape3D.d.ts.map +1 -1
  138. package/src/core/geom/3d/shape/MeshShape3D.js +27 -106
  139. package/src/core/geom/3d/shape/Triangle3D.d.ts +1 -1
  140. package/src/core/geom/3d/shape/Triangle3D.d.ts.map +1 -1
  141. package/src/core/geom/3d/shape/Triangle3D.js +10 -31
  142. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_carve_outside_surface.d.ts.map +1 -1
  143. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_carve_outside_surface.js +8 -57
  144. package/src/core/geom/3d/topology/simplify/decimate_edge_collapse_snap.d.ts +8 -1
  145. package/src/core/geom/3d/topology/simplify/decimate_edge_collapse_snap.d.ts.map +1 -1
  146. package/src/core/geom/3d/topology/simplify/decimate_edge_collapse_snap.js +281 -272
  147. package/src/core/geom/3d/topology/simplify/quadratic/build_vertex_quadrics.d.ts.map +1 -1
  148. package/src/core/geom/3d/topology/simplify/quadratic/build_vertex_quadrics.js +3 -4
  149. package/src/core/geom/3d/topology/simplify/quadratic/quadric3_set_from_plane_through_point.d.ts +22 -0
  150. package/src/core/geom/3d/topology/simplify/quadratic/quadric3_set_from_plane_through_point.d.ts.map +1 -0
  151. package/src/core/geom/3d/topology/simplify/quadratic/quadric3_set_from_plane_through_point.js +27 -0
  152. package/src/core/geom/3d/topology/struct/binary/io/bt_boundary_loop.d.ts +24 -0
  153. package/src/core/geom/3d/topology/struct/binary/io/bt_boundary_loop.d.ts.map +1 -0
  154. package/src/core/geom/3d/topology/struct/binary/io/bt_boundary_loop.js +73 -0
  155. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_close_boundary_holes.d.ts.map +1 -1
  156. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_close_boundary_holes.js +2 -53
  157. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compute_vertex_quadrics.d.ts.map +1 -1
  158. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compute_vertex_quadrics.js +3 -4
  159. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_fill_small_holes.d.ts.map +1 -1
  160. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_fill_small_holes.js +3 -33
  161. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_from_indexed_geometry.d.ts.map +1 -1
  162. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_from_indexed_geometry.js +4 -37
  163. package/src/core/geom/3d/topology/struct/binary/io/face/bt_face_poke.d.ts.map +1 -1
  164. package/src/core/geom/3d/topology/struct/binary/io/face/bt_face_poke.js +3 -32
  165. package/src/core/geom/3d/topology/struct/binary/query/bt_face_read_triangle.d.ts +32 -0
  166. package/src/core/geom/3d/topology/struct/binary/query/bt_face_read_triangle.d.ts.map +1 -0
  167. package/src/core/geom/3d/topology/struct/binary/query/bt_face_read_triangle.js +66 -0
  168. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_face_find_path.d.ts.map +1 -1
  169. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_face_find_path.js +703 -728
  170. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_face_find_path_polyanya.d.ts.map +1 -1
  171. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_face_find_path_polyanya.js +13 -24
  172. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_sample_interior_grid_points.d.ts.map +1 -1
  173. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_sample_interior_grid_points.js +8 -54
  174. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_surface_ray_parity.d.ts +35 -0
  175. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_surface_ray_parity.d.ts.map +1 -0
  176. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_surface_ray_parity.js +82 -0
  177. package/src/core/geom/3d/triangle/tri3_closest_point.d.ts +29 -0
  178. package/src/core/geom/3d/triangle/tri3_closest_point.d.ts.map +1 -0
  179. package/src/core/geom/3d/triangle/tri3_closest_point.js +65 -0
  180. package/src/core/geom/3d/triangle/tri3_flatten_isometric.d.ts +42 -0
  181. package/src/core/geom/3d/triangle/tri3_flatten_isometric.d.ts.map +1 -0
  182. package/src/core/geom/3d/triangle/tri3_flatten_isometric.js +79 -0
  183. package/src/core/geom/Vector2.d.ts.map +1 -1
  184. package/src/core/geom/Vector2.js +2 -1
  185. package/src/core/geom/random/random_orthonormal_basis.d.ts +21 -0
  186. package/src/core/geom/random/random_orthonormal_basis.d.ts.map +1 -0
  187. package/src/core/geom/random/random_orthonormal_basis.js +31 -0
  188. package/src/core/geom/vec2/v2_rotate.d.ts +18 -0
  189. package/src/core/geom/vec2/v2_rotate.d.ts.map +1 -0
  190. package/src/core/geom/vec2/v2_rotate.js +21 -0
  191. package/src/core/geom/vec2/v2_rotate_cos_sin.d.ts +19 -0
  192. package/src/core/geom/vec2/v2_rotate_cos_sin.d.ts.map +1 -0
  193. package/src/core/geom/vec2/v2_rotate_cos_sin.js +21 -0
  194. package/src/core/graph/csr/csr_graph_from_undirected_edge_list.d.ts +30 -0
  195. package/src/core/graph/csr/csr_graph_from_undirected_edge_list.d.ts.map +1 -0
  196. package/src/core/graph/csr/csr_graph_from_undirected_edge_list.js +94 -0
  197. package/src/core/graph/layout/CircleLayout.d.ts.map +1 -1
  198. package/src/core/graph/layout/CircleLayout.js +6 -29
  199. package/src/core/graph/layout/box/BoxLayouter.d.ts.map +1 -1
  200. package/src/core/graph/layout/box/BoxLayouter.js +2 -26
  201. package/src/core/graph/layout/box/position_box_next_to_box.d.ts.map +1 -1
  202. package/src/core/graph/layout/box/position_box_next_to_box.js +2 -1
  203. package/src/core/graph/layout/computeDisconnectedSubGraphs.d.ts.map +1 -1
  204. package/src/core/graph/layout/computeDisconnectedSubGraphs.js +14 -3
  205. package/src/core/graph/layout/graph_peel_topological_layer.d.ts +25 -0
  206. package/src/core/graph/layout/graph_peel_topological_layer.d.ts.map +1 -0
  207. package/src/core/graph/layout/graph_peel_topological_layer.js +45 -0
  208. package/src/core/math/hash/hash_mix2.d.ts +21 -0
  209. package/src/core/math/hash/hash_mix2.d.ts.map +1 -0
  210. package/src/core/math/hash/hash_mix2.js +22 -0
  211. package/src/core/math/hash/hash_mix3.d.ts +16 -0
  212. package/src/core/math/hash/hash_mix3.d.ts.map +1 -0
  213. package/src/core/math/hash/hash_mix3.js +19 -0
  214. package/src/core/math/lookup/LUT_HEATMAP.d.ts +17 -0
  215. package/src/core/math/lookup/LUT_HEATMAP.d.ts.map +1 -0
  216. package/src/core/math/lookup/LUT_HEATMAP.js +30 -0
  217. package/src/core/math/noise/curl_noise_3d.d.ts +6 -0
  218. package/src/core/math/noise/curl_noise_3d.d.ts.map +1 -1
  219. package/src/core/math/noise/curl_noise_3d.js +18 -27
  220. package/src/core/math/statistics/hammersley_sequence.d.ts +28 -16
  221. package/src/core/math/statistics/hammersley_sequence.d.ts.map +1 -1
  222. package/src/core/math/statistics/hammersley_sequence.js +53 -34
  223. package/src/core/science/units/MEASUREMENT_UNITS_SI.d.ts +3 -0
  224. package/src/core/science/units/MEASUREMENT_UNITS_SI.d.ts.map +1 -1
  225. package/src/core/science/units/MEASUREMENT_UNITS_SI.js +5 -16
  226. package/src/engine/animation/TransitionFunctions.d.ts +31 -0
  227. package/src/engine/animation/TransitionFunctions.d.ts.map +1 -1
  228. package/src/engine/animation/TransitionFunctions.js +44 -0
  229. package/src/engine/animation/curve/animation_curve_binary_codec.d.ts +29 -0
  230. package/src/engine/animation/curve/animation_curve_binary_codec.d.ts.map +1 -0
  231. package/src/engine/animation/curve/animation_curve_binary_codec.js +60 -0
  232. package/src/engine/animation/curve/draw/canvas_point_to_curve.d.ts +16 -0
  233. package/src/engine/animation/curve/draw/canvas_point_to_curve.d.ts.map +1 -0
  234. package/src/engine/animation/curve/draw/canvas_point_to_curve.js +22 -0
  235. package/src/engine/animation/curve/draw/curve_point_to_canvas.d.ts +27 -0
  236. package/src/engine/animation/curve/draw/curve_point_to_canvas.d.ts.map +1 -0
  237. package/src/engine/animation/curve/draw/curve_point_to_canvas.js +38 -0
  238. package/src/engine/animation/curve/draw/position_canvas_to_curve.d.ts +2 -0
  239. package/src/engine/animation/curve/draw/position_canvas_to_curve.d.ts.map +1 -1
  240. package/src/engine/animation/curve/draw/position_canvas_to_curve.js +22 -24
  241. package/src/engine/animation/curve/draw/position_curve_to_canvas.d.ts +2 -0
  242. package/src/engine/animation/curve/draw/position_curve_to_canvas.d.ts.map +1 -1
  243. package/src/engine/animation/curve/draw/position_curve_to_canvas.js +22 -25
  244. package/src/engine/animation/curve/editor/canvas2dDrawWorldAxisLabels.d.ts.map +1 -1
  245. package/src/engine/animation/curve/editor/canvas2dDrawWorldAxisLabels.js +3 -2
  246. package/src/engine/animation/curve/editor/canvas2dPlotCurvePoints.d.ts.map +1 -1
  247. package/src/engine/animation/curve/editor/canvas2dPlotCurvePoints.js +14 -14
  248. package/src/engine/animation/removeEntityWithMeshParticlesEffect.d.ts.map +1 -1
  249. package/src/engine/animation/removeEntityWithMeshParticlesEffect.js +5 -0
  250. package/src/engine/asset/loaders/GLTFAssetLoader.d.ts.map +1 -1
  251. package/src/engine/asset/loaders/GLTFAssetLoader.js +25 -51
  252. package/src/engine/asset/loaders/material/StaticMaterialCache.d.ts +3 -2
  253. package/src/engine/asset/loaders/material/StaticMaterialCache.d.ts.map +1 -1
  254. package/src/engine/asset/loaders/material/StaticMaterialCache.js +16 -55
  255. package/src/engine/asset/loaders/material/TextureCoalescer.d.ts +32 -0
  256. package/src/engine/asset/loaders/material/TextureCoalescer.d.ts.map +1 -0
  257. package/src/engine/asset/loaders/material/TextureCoalescer.js +68 -0
  258. package/src/engine/asset/loaders/material/traverseMaterialTextures.d.ts +10 -2
  259. package/src/engine/asset/loaders/material/traverseMaterialTextures.d.ts.map +1 -1
  260. package/src/engine/asset/loaders/material/traverseMaterialTextures.js +10 -2
  261. package/src/engine/control/first-person/abilities/Mantle.d.ts.map +1 -1
  262. package/src/engine/control/first-person/abilities/Mantle.js +10 -26
  263. package/src/engine/ecs/dynamic_actions/actions/definition/WeightedRandomActionDescription.d.ts.map +1 -1
  264. package/src/engine/ecs/dynamic_actions/actions/definition/WeightedRandomActionDescription.js +5 -0
  265. package/src/engine/ecs/evaluation/complex/PointFitnessGaussianBlur.d.ts +22 -2
  266. package/src/engine/ecs/evaluation/complex/PointFitnessGaussianBlur.d.ts.map +1 -1
  267. package/src/engine/ecs/evaluation/complex/PointFitnessGaussianBlur.js +92 -41
  268. package/src/engine/ecs/gui/hud/HeadsUpDisplaySystem.d.ts +3 -1
  269. package/src/engine/ecs/gui/hud/HeadsUpDisplaySystem.d.ts.map +1 -1
  270. package/src/engine/ecs/gui/hud/HeadsUpDisplaySystem.js +14 -16
  271. package/src/engine/ecs/gui/parallax/GuiElementParallaxSystem.d.ts.map +1 -1
  272. package/src/engine/ecs/gui/parallax/GuiElementParallaxSystem.js +16 -29
  273. package/src/engine/ecs/ik/OneBoneSurfaceAlignmentSolver.d.ts.map +1 -1
  274. package/src/engine/ecs/ik/OneBoneSurfaceAlignmentSolver.js +12 -74
  275. package/src/engine/ecs/ik/TwoBoneInverseKinematicsSolver.d.ts.map +1 -1
  276. package/src/engine/ecs/ik/TwoBoneInverseKinematicsSolver.js +10 -73
  277. package/src/engine/ecs/ik/ik_probe_terrain_contact.d.ts +38 -0
  278. package/src/engine/ecs/ik/ik_probe_terrain_contact.d.ts.map +1 -0
  279. package/src/engine/ecs/ik/ik_probe_terrain_contact.js +119 -0
  280. package/src/engine/ecs/terrain/util/paintTerrainOverlayViaLookupTable.d.ts +0 -1
  281. package/src/engine/ecs/terrain/util/paintTerrainOverlayViaLookupTable.d.ts.map +1 -1
  282. package/src/engine/ecs/terrain/util/paintTerrainOverlayViaLookupTable.js +2 -14
  283. package/src/engine/graphics/FULL_SCREEN_QUAD_VERTEX_SHADER.d.ts +12 -1
  284. package/src/engine/graphics/FULL_SCREEN_QUAD_VERTEX_SHADER.d.ts.map +1 -1
  285. package/src/engine/graphics/FULL_SCREEN_QUAD_VERTEX_SHADER.js +14 -10
  286. package/src/engine/graphics/ecs/camera/auto_set_camera_clipping_planes.d.ts.map +1 -1
  287. package/src/engine/graphics/ecs/camera/auto_set_camera_clipping_planes.js +7 -28
  288. package/src/engine/graphics/ecs/highlight/renderer/HighlightDecodeShader.d.ts +1 -2
  289. package/src/engine/graphics/ecs/highlight/renderer/HighlightDecodeShader.d.ts.map +1 -1
  290. package/src/engine/graphics/ecs/highlight/renderer/HighlightDecodeShader.js +4 -12
  291. package/src/engine/graphics/ecs/highlight/renderer/OutlineRenderer.d.ts.map +1 -1
  292. package/src/engine/graphics/ecs/highlight/renderer/OutlineRenderer.js +2 -11
  293. package/src/engine/graphics/ecs/highlight/renderer/makeBlurShader_9.d.ts +1 -2
  294. package/src/engine/graphics/ecs/highlight/renderer/makeBlurShader_9.d.ts.map +1 -1
  295. package/src/engine/graphics/ecs/highlight/renderer/makeBlurShader_9.js +3 -11
  296. package/src/engine/graphics/ecs/highlight/renderer/makeDilationShader.d.ts +1 -2
  297. package/src/engine/graphics/ecs/highlight/renderer/makeDilationShader.d.ts.map +1 -1
  298. package/src/engine/graphics/ecs/highlight/renderer/makeDilationShader.js +3 -11
  299. package/src/engine/graphics/ecs/highlight/renderer/make_screen_space_quad_material.d.ts +33 -0
  300. package/src/engine/graphics/ecs/highlight/renderer/make_screen_space_quad_material.d.ts.map +1 -0
  301. package/src/engine/graphics/ecs/highlight/renderer/make_screen_space_quad_material.js +44 -0
  302. package/src/engine/graphics/ecs/highlight/system/HighlightSystemBase.d.ts +55 -0
  303. package/src/engine/graphics/ecs/highlight/system/HighlightSystemBase.d.ts.map +1 -0
  304. package/src/engine/graphics/ecs/highlight/system/HighlightSystemBase.js +205 -0
  305. package/src/engine/graphics/ecs/highlight/system/MeshHighlightSystem.d.ts +4 -45
  306. package/src/engine/graphics/ecs/highlight/system/MeshHighlightSystem.d.ts.map +1 -1
  307. package/src/engine/graphics/ecs/highlight/system/MeshHighlightSystem.js +39 -162
  308. package/src/engine/graphics/ecs/highlight/system/RenderableHighlightSystem.d.ts +4 -51
  309. package/src/engine/graphics/ecs/highlight/system/RenderableHighlightSystem.d.ts.map +1 -1
  310. package/src/engine/graphics/ecs/highlight/system/RenderableHighlightSystem.js +34 -164
  311. package/src/engine/graphics/ecs/highlight/system/ShadedGeometryHighlightSystem.d.ts +5 -57
  312. package/src/engine/graphics/ecs/highlight/system/ShadedGeometryHighlightSystem.d.ts.map +1 -1
  313. package/src/engine/graphics/ecs/highlight/system/ShadedGeometryHighlightSystem.js +48 -175
  314. package/src/engine/graphics/ecs/light/shadow/extend_shadow_camera_near_for_casters.d.ts.map +1 -1
  315. package/src/engine/graphics/ecs/light/shadow/extend_shadow_camera_near_for_casters.js +7 -25
  316. package/src/engine/graphics/ecs/make_bvh_depth_range_computer.d.ts +26 -0
  317. package/src/engine/graphics/ecs/make_bvh_depth_range_computer.d.ts.map +1 -0
  318. package/src/engine/graphics/ecs/make_bvh_depth_range_computer.js +33 -0
  319. package/src/engine/graphics/ecs/mesh/MeshSystem.d.ts.map +1 -1
  320. package/src/engine/graphics/ecs/mesh/MeshSystem.js +2 -5
  321. package/src/engine/graphics/ecs/mesh-v2/ShadedGeometrySystem.d.ts.map +1 -1
  322. package/src/engine/graphics/ecs/mesh-v2/ShadedGeometrySystem.js +2 -5
  323. package/src/engine/graphics/ecs/render_layers_compute_depth_range.d.ts +35 -0
  324. package/src/engine/graphics/ecs/render_layers_compute_depth_range.d.ts.map +1 -0
  325. package/src/engine/graphics/ecs/render_layers_compute_depth_range.js +64 -0
  326. package/src/engine/graphics/ecs/trail2d/Trail2DSystem.d.ts.map +1 -1
  327. package/src/engine/graphics/ecs/trail2d/Trail2DSystem.js +2 -5
  328. package/src/engine/graphics/ecs/trail3d/Trail3DSystem.d.ts.map +1 -1
  329. package/src/engine/graphics/ecs/trail3d/Trail3DSystem.js +2 -5
  330. package/src/engine/graphics/ecs/water/WaterSystem.d.ts.map +1 -1
  331. package/src/engine/graphics/ecs/water/WaterSystem.js +2 -5
  332. package/src/engine/graphics/geometry/buffered/query/bvh32_geometry_nearest.d.ts.map +1 -1
  333. package/src/engine/graphics/geometry/buffered/query/bvh32_geometry_nearest.js +22 -94
  334. package/src/engine/graphics/geometry/buffered/query/bvh32_geometry_overlap_clipping_volume.d.ts.map +1 -1
  335. package/src/engine/graphics/geometry/buffered/query/bvh32_geometry_overlap_clipping_volume.js +17 -57
  336. package/src/engine/graphics/geometry/buffered/query/bvh32_geometry_raycast.d.ts.map +1 -1
  337. package/src/engine/graphics/geometry/buffered/query/bvh32_geometry_raycast.js +17 -57
  338. package/src/engine/graphics/geometry/buffered/query/nearest_point_on_triangle_record.d.ts +30 -0
  339. package/src/engine/graphics/geometry/buffered/query/nearest_point_on_triangle_record.d.ts.map +1 -0
  340. package/src/engine/graphics/geometry/buffered/query/nearest_point_on_triangle_record.js +87 -0
  341. package/src/engine/graphics/geometry/buffered/query/query_bvh_geometry_nearest.d.ts.map +1 -1
  342. package/src/engine/graphics/geometry/buffered/query/query_bvh_geometry_nearest.js +11 -55
  343. package/src/engine/graphics/geometry/buffered/read_triangle_vertices.d.ts +29 -0
  344. package/src/engine/graphics/geometry/buffered/read_triangle_vertices.d.ts.map +1 -0
  345. package/src/engine/graphics/geometry/buffered/read_triangle_vertices.js +102 -0
  346. package/src/engine/graphics/impostors/octahedral/shader/ImpostorShaderDepthV0.d.ts.map +1 -1
  347. package/src/engine/graphics/impostors/octahedral/shader/ImpostorShaderDepthV0.js +17 -122
  348. package/src/engine/graphics/impostors/octahedral/shader/ImpostorShaderLitV0.d.ts.map +1 -1
  349. package/src/engine/graphics/impostors/octahedral/shader/ImpostorShaderLitV0.js +15 -194
  350. package/src/engine/graphics/impostors/octahedral/shader/ImpostorShaderNormalsV0.d.ts.map +1 -1
  351. package/src/engine/graphics/impostors/octahedral/shader/ImpostorShaderNormalsV0.js +12 -108
  352. package/src/engine/graphics/impostors/octahedral/shader/ImpostorShaderPerPixelV0.d.ts.map +1 -1
  353. package/src/engine/graphics/impostors/octahedral/shader/ImpostorShaderPerPixelV0.js +14 -183
  354. package/src/engine/graphics/impostors/octahedral/shader/ImpostorShaderV0.d.ts.map +1 -1
  355. package/src/engine/graphics/impostors/octahedral/shader/ImpostorShaderV0.js +12 -154
  356. package/src/engine/graphics/impostors/octahedral/shader/ImpostorShaderViewportDepthV0.d.ts.map +1 -1
  357. package/src/engine/graphics/impostors/octahedral/shader/ImpostorShaderViewportDepthV0.js +10 -94
  358. package/src/engine/graphics/impostors/octahedral/shader/ImpostorShaderWireframeV0.d.ts.map +1 -1
  359. package/src/engine/graphics/impostors/octahedral/shader/ImpostorShaderWireframeV0.js +8 -72
  360. package/src/engine/graphics/impostors/octahedral/shader/glsl/common.glsl +10 -0
  361. package/src/engine/graphics/impostors/voxel/bake/read_atlas_to_samples.d.ts.map +1 -1
  362. package/src/engine/graphics/impostors/voxel/bake/read_atlas_to_samples.js +79 -58
  363. package/src/engine/graphics/impostors/voxel/shader/VoxelImpostorShaderDepthV0.d.ts.map +1 -1
  364. package/src/engine/graphics/impostors/voxel/shader/VoxelImpostorShaderDepthV0.js +11 -22
  365. package/src/engine/graphics/impostors/voxel/shader/VoxelImpostorShaderLitV0.d.ts.map +1 -1
  366. package/src/engine/graphics/impostors/voxel/shader/VoxelImpostorShaderLitV0.js +7 -16
  367. package/src/engine/graphics/impostors/voxel/shader/VoxelImpostorShaderNormalsV0.d.ts.map +1 -1
  368. package/src/engine/graphics/impostors/voxel/shader/VoxelImpostorShaderNormalsV0.js +5 -9
  369. package/src/engine/graphics/impostors/voxel/shader/VoxelImpostorShaderV0.d.ts.map +1 -1
  370. package/src/engine/graphics/impostors/voxel/shader/VoxelImpostorShaderV0.js +4 -19
  371. package/src/engine/graphics/impostors/voxel/shader/VoxelImpostorShaderViewportDepthV0.d.ts.map +1 -1
  372. package/src/engine/graphics/impostors/voxel/shader/VoxelImpostorShaderViewportDepthV0.js +5 -9
  373. package/src/engine/graphics/material/SplatMaterial.d.ts.map +1 -1
  374. package/src/engine/graphics/material/SplatMaterial.js +2 -17
  375. package/src/engine/graphics/material/WaterMaterial.d.ts.map +1 -1
  376. package/src/engine/graphics/material/WaterMaterial.js +3 -18
  377. package/src/engine/graphics/material/manager/compilers/CoalesceTextures.d.ts +4 -4
  378. package/src/engine/graphics/material/manager/compilers/CoalesceTextures.d.ts.map +1 -1
  379. package/src/engine/graphics/material/manager/compilers/CoalesceTextures.js +25 -70
  380. package/src/engine/graphics/particles/ecs/ParticleEmitterSystem.d.ts.map +1 -1
  381. package/src/engine/graphics/particles/ecs/ParticleEmitterSystem.js +2 -5
  382. package/src/engine/graphics/particles/node-based/codegen/glsl/getTypeByteSize.d.ts +8 -11
  383. package/src/engine/graphics/particles/node-based/codegen/glsl/getTypeByteSize.d.ts.map +1 -1
  384. package/src/engine/graphics/particles/node-based/codegen/glsl/getTypeByteSize.js +20 -21
  385. package/src/engine/graphics/particles/node-based/nodes/particle_data_type_component_count.d.ts +39 -0
  386. package/src/engine/graphics/particles/node-based/nodes/particle_data_type_component_count.d.ts.map +1 -0
  387. package/src/engine/graphics/particles/node-based/nodes/particle_data_type_component_count.js +45 -0
  388. package/src/engine/graphics/particles/node-based/particle/ParticleAttributeSpecification.d.ts +6 -8
  389. package/src/engine/graphics/particles/node-based/particle/ParticleAttributeSpecification.d.ts.map +1 -1
  390. package/src/engine/graphics/particles/node-based/particle/ParticleAttributeSpecification.js +2 -13
  391. package/src/engine/graphics/particles/particular/engine/utils/distributeParticlesOnObject3D.d.ts.map +1 -1
  392. package/src/engine/graphics/particles/particular/engine/utils/distributeParticlesOnObject3D.js +16 -78
  393. package/src/engine/graphics/particles/particular/engine/utils/distribute_points_on_indexed_triangles.d.ts +45 -0
  394. package/src/engine/graphics/particles/particular/engine/utils/distribute_points_on_indexed_triangles.d.ts.map +1 -0
  395. package/src/engine/graphics/particles/particular/engine/utils/distribute_points_on_indexed_triangles.js +168 -0
  396. package/src/engine/graphics/particles/particular/engine/utils/distrubuteParticlesOnMesh.d.ts.map +1 -1
  397. package/src/engine/graphics/particles/particular/engine/utils/distrubuteParticlesOnMesh.js +11 -115
  398. package/src/engine/graphics/postprocess/threejs/shaders/CopyShader.d.ts +24 -16
  399. package/src/engine/graphics/postprocess/threejs/shaders/CopyShader.d.ts.map +1 -1
  400. package/src/engine/graphics/postprocess/threejs/shaders/CopyShader.js +15 -47
  401. package/src/engine/graphics/render/buffer/simple-fx/ao/AmbientOcclusionPostProcessEffect.d.ts +4 -0
  402. package/src/engine/graphics/render/buffer/simple-fx/ao/AmbientOcclusionPostProcessEffect.d.ts.map +1 -1
  403. package/src/engine/graphics/render/buffer/simple-fx/ao/AmbientOcclusionPostProcessEffect.js +4 -0
  404. package/src/engine/graphics/sh3/build_injected_shader.d.ts +51 -0
  405. package/src/engine/graphics/sh3/build_injected_shader.d.ts.map +1 -0
  406. package/src/engine/graphics/sh3/build_injected_shader.js +56 -0
  407. package/src/engine/graphics/sh3/gi/material/MaterialTransformer.d.ts.map +1 -1
  408. package/src/engine/graphics/sh3/gi/material/MaterialTransformer.js +10 -1
  409. package/src/engine/graphics/sh3/gi/material/chunk_lpv_get_irradiance_at.d.ts.map +1 -1
  410. package/src/engine/graphics/sh3/gi/material/chunk_lpv_get_irradiance_at.js +17 -0
  411. package/src/engine/graphics/sh3/gi/material/common.d.ts +1 -1
  412. package/src/engine/graphics/sh3/gi/material/common.js +6 -222
  413. package/src/engine/graphics/sh3/gi/material/space_fragment/build_fragment_shader.d.ts.map +1 -1
  414. package/src/engine/graphics/sh3/gi/material/space_fragment/build_fragment_shader.js +7 -11
  415. package/src/engine/graphics/sh3/gi/material/space_fragment/build_vertex_shader.d.ts.map +1 -1
  416. package/src/engine/graphics/sh3/gi/material/space_fragment/build_vertex_shader.js +27 -29
  417. package/src/engine/graphics/sh3/gi/material/space_vertex/build_fragment_shader.d.ts.map +1 -1
  418. package/src/engine/graphics/sh3/gi/material/space_vertex/build_fragment_shader.js +7 -11
  419. package/src/engine/graphics/sh3/gi/material/space_vertex/build_vertex_shader.d.ts.map +1 -1
  420. package/src/engine/graphics/sh3/gi/material/space_vertex/build_vertex_shader.js +7 -12
  421. package/src/engine/graphics/sh3/grid_dilate_average.d.ts +38 -0
  422. package/src/engine/graphics/sh3/grid_dilate_average.d.ts.map +1 -0
  423. package/src/engine/graphics/sh3/grid_dilate_average.js +175 -0
  424. package/src/engine/graphics/sh3/lightmap/lightmap_dilate.d.ts +4 -0
  425. package/src/engine/graphics/sh3/lightmap/lightmap_dilate.d.ts.map +1 -1
  426. package/src/engine/graphics/sh3/lightmap/lightmap_dilate.js +7 -70
  427. package/src/engine/graphics/sh3/lpg/light_probe_grid_dilate.d.ts +6 -0
  428. package/src/engine/graphics/sh3/lpg/light_probe_grid_dilate.d.ts.map +1 -1
  429. package/src/engine/graphics/sh3/lpg/light_probe_grid_dilate.js +14 -113
  430. package/src/engine/graphics/sh3/lpg/material/chunk_lpg_get_irradiance_at.d.ts.map +1 -1
  431. package/src/engine/graphics/sh3/lpg/material/chunk_lpg_get_irradiance_at.js +7 -0
  432. package/src/engine/graphics/sh3/lpg/material/space_fragment/build_fragment_shader.d.ts.map +1 -1
  433. package/src/engine/graphics/sh3/lpg/material/space_fragment/build_fragment_shader.js +5 -8
  434. package/src/engine/graphics/sh3/lpg/material/space_fragment/build_vertex_shader.d.ts.map +1 -1
  435. package/src/engine/graphics/sh3/lpg/material/space_fragment/build_vertex_shader.js +5 -8
  436. package/src/engine/graphics/sh3/lpg/material/space_vertex/build_fragment_shader.d.ts.map +1 -1
  437. package/src/engine/graphics/sh3/lpg/material/space_vertex/build_fragment_shader.js +5 -8
  438. package/src/engine/graphics/sh3/lpg/material/space_vertex/build_vertex_shader.d.ts.map +1 -1
  439. package/src/engine/graphics/sh3/lpg/material/space_vertex/build_vertex_shader.js +5 -8
  440. package/src/engine/graphics/sh3/lpv/depth/chunk_probe_depth_sampling.d.ts +38 -0
  441. package/src/engine/graphics/sh3/lpv/depth/chunk_probe_depth_sampling.d.ts.map +1 -0
  442. package/src/engine/graphics/sh3/lpv/depth/chunk_probe_depth_sampling.js +265 -0
  443. package/src/engine/graphics/sh3/lpv/depth/octahedral/shader/visualize.frag.glsl +11 -0
  444. package/src/engine/graphics/sh3/lpv/depth/octahedral/v2/VLPDepthMapVisualisation.d.ts.map +1 -1
  445. package/src/engine/graphics/sh3/lpv/depth/octahedral/v2/VLPDepthMapVisualisation.js +5 -1
  446. package/src/engine/graphics/sh3/lpv/depth/octahedral/v2/visualise.frag.glsl +189 -419
  447. package/src/engine/graphics/sh3/shader/visualize.frag.d.ts +1 -1
  448. package/src/engine/graphics/sh3/shader/visualize.frag.js +7 -0
  449. package/src/engine/graphics/shaders/chunks/glsl_octahedral_impostor.d.ts +102 -0
  450. package/src/engine/graphics/shaders/chunks/glsl_octahedral_impostor.d.ts.map +1 -0
  451. package/src/engine/graphics/shaders/chunks/glsl_octahedral_impostor.js +277 -0
  452. package/src/engine/graphics/shaders/chunks/glsl_packing.d.ts +29 -0
  453. package/src/engine/graphics/shaders/chunks/glsl_packing.d.ts.map +1 -0
  454. package/src/engine/graphics/shaders/chunks/glsl_packing.js +30 -0
  455. package/src/engine/graphics/shaders/chunks/glsl_voxel_impostor_splat.d.ts +40 -0
  456. package/src/engine/graphics/shaders/chunks/glsl_voxel_impostor_splat.d.ts.map +1 -0
  457. package/src/engine/graphics/shaders/chunks/glsl_voxel_impostor_splat.js +57 -0
  458. package/src/engine/graphics/shaders/lib/make_clouds_uniforms.d.ts +22 -0
  459. package/src/engine/graphics/shaders/lib/make_clouds_uniforms.d.ts.map +1 -0
  460. package/src/engine/graphics/shaders/lib/make_clouds_uniforms.js +39 -0
  461. package/src/engine/graphics/texture/attribute_spec_to_three_format.d.ts +6 -0
  462. package/src/engine/graphics/texture/attribute_spec_to_three_format.d.ts.map +1 -1
  463. package/src/engine/graphics/texture/attribute_spec_to_three_format.js +9 -9
  464. package/src/engine/graphics/texture/channel_count_to_format_prefix.d.ts +14 -0
  465. package/src/engine/graphics/texture/channel_count_to_format_prefix.d.ts.map +1 -0
  466. package/src/engine/graphics/texture/channel_count_to_format_prefix.js +26 -0
  467. package/src/engine/graphics/texture/computeThreeTextureInternalFormatFromDataType.d.ts.map +1 -1
  468. package/src/engine/graphics/texture/computeThreeTextureInternalFormatFromDataType.js +2 -20
  469. package/src/engine/graphics/texture/normalized_internal_format.d.ts.map +1 -1
  470. package/src/engine/graphics/texture/normalized_internal_format.js +2 -20
  471. package/src/engine/graphics/texture/sampler/convertTexture2Sampler2D.d.ts.map +1 -1
  472. package/src/engine/graphics/texture/sampler/convertTexture2Sampler2D.js +9 -3
  473. package/src/engine/input/devices/gamepad/GamepadDeviceLayout.d.ts +54 -0
  474. package/src/engine/input/devices/gamepad/GamepadDeviceLayout.d.ts.map +1 -0
  475. package/src/engine/input/devices/gamepad/GamepadDeviceLayout.js +323 -0
  476. package/src/engine/input/devices/gamepad/GamepadHandle.d.ts +21 -2
  477. package/src/engine/input/devices/gamepad/GamepadHandle.d.ts.map +1 -1
  478. package/src/engine/input/devices/gamepad/GamepadHandle.js +39 -11
  479. package/src/engine/input/devices/gamepad/GamepadLayout.d.ts +128 -0
  480. package/src/engine/input/devices/gamepad/GamepadLayout.d.ts.map +1 -0
  481. package/src/engine/input/devices/gamepad/GamepadLayout.js +160 -0
  482. package/src/engine/input/devices/gamepad/GamepadStandardMapping.d.ts +10 -0
  483. package/src/engine/input/devices/gamepad/GamepadStandardMapping.d.ts.map +1 -1
  484. package/src/engine/input/devices/gamepad/GamepadStandardMapping.js +11 -0
  485. package/src/engine/input/devices/gamepad/GamepadStick.d.ts +5 -1
  486. package/src/engine/input/devices/gamepad/GamepadStick.d.ts.map +1 -1
  487. package/src/engine/input/devices/gamepad/GamepadStick.js +5 -1
  488. package/src/engine/input/devices/gamepad/gamepad_layout_database.d.ts +27 -0
  489. package/src/engine/input/devices/gamepad/gamepad_layout_database.d.ts.map +1 -0
  490. package/src/engine/input/devices/gamepad/gamepad_layout_database.js +144 -0
  491. package/src/engine/input/devices/gamepad/gamepad_read_hat_axis.d.ts +25 -0
  492. package/src/engine/input/devices/gamepad/gamepad_read_hat_axis.d.ts.map +1 -0
  493. package/src/engine/input/devices/gamepad/gamepad_read_hat_axis.js +61 -0
  494. package/src/engine/input/devices/gamepad/prototypeGamepadTester.d.ts +2 -0
  495. package/src/engine/input/devices/gamepad/prototypeGamepadTester.d.ts.map +1 -0
  496. package/src/engine/input/devices/gamepad/prototypeGamepadTester.js +2124 -0
  497. package/src/engine/input/ecs/util/TopDownCameraControllerHelper.d.ts.map +1 -1
  498. package/src/engine/input/ecs/util/TopDownCameraControllerHelper.js +3 -33
  499. package/src/engine/intelligence/behavior/util/RandomDelayBehavior.d.ts.map +1 -1
  500. package/src/engine/intelligence/behavior/util/RandomDelayBehavior.js +5 -0
  501. package/src/engine/network/adapters/QuaternionInterpolationAdapter.d.ts.map +1 -1
  502. package/src/engine/network/adapters/QuaternionInterpolationAdapter.js +18 -19
  503. package/src/engine/network/adapters/TransformInterpolationAdapter.d.ts.map +1 -1
  504. package/src/engine/network/adapters/TransformInterpolationAdapter.js +18 -13
  505. package/src/engine/network/orchestrator/NetworkPeer.d.ts.map +1 -1
  506. package/src/engine/network/orchestrator/NetworkPeer.js +2 -1
  507. package/src/engine/network/sim/ActionLog.d.ts +9 -6
  508. package/src/engine/network/sim/ActionLog.d.ts.map +1 -1
  509. package/src/engine/network/sim/ActionLog.js +43 -80
  510. package/src/engine/network/sim/SmoothingState.d.ts.map +1 -1
  511. package/src/engine/network/sim/SmoothingState.js +27 -28
  512. package/src/engine/network/state/FrameBufferRing.d.ts +84 -0
  513. package/src/engine/network/state/FrameBufferRing.d.ts.map +1 -0
  514. package/src/engine/network/state/FrameBufferRing.js +168 -0
  515. package/src/engine/network/state/InputRing.d.ts +14 -12
  516. package/src/engine/network/state/InputRing.d.ts.map +1 -1
  517. package/src/engine/network/state/InputRing.js +37 -76
  518. package/src/engine/network/transport/LoopbackTransport.d.ts.map +1 -1
  519. package/src/engine/network/transport/LoopbackTransport.js +10 -31
  520. package/src/engine/network/transport/Transport.d.ts +42 -2
  521. package/src/engine/network/transport/Transport.d.ts.map +1 -1
  522. package/src/engine/network/transport/Transport.js +87 -3
  523. package/src/engine/network/transport/adapters/NodeUDPTransport.d.ts.map +1 -1
  524. package/src/engine/network/transport/adapters/NodeUDPTransport.js +7 -33
  525. package/src/engine/network/transport/adapters/SimulatedTransport.d.ts.map +1 -1
  526. package/src/engine/network/transport/adapters/SimulatedTransport.js +17 -28
  527. package/src/engine/network/transport/adapters/WebRTCDataChannelTransport.d.ts.map +1 -1
  528. package/src/engine/network/transport/adapters/WebRTCDataChannelTransport.js +6 -32
  529. package/src/engine/network/transport/adapters/WebSocketTransport.d.ts.map +1 -1
  530. package/src/engine/network/transport/adapters/WebSocketTransport.js +6 -32
  531. package/src/engine/network/transport/adapters/WebTransportTransport.d.ts.map +1 -1
  532. package/src/engine/network/transport/adapters/WebTransportTransport.js +11 -22
  533. package/src/engine/physics/constraint/solve_constraints.d.ts.map +1 -1
  534. package/src/engine/physics/constraint/solve_constraints.js +18 -48
  535. package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
  536. package/src/engine/physics/fluid/ecs/FluidSystem.d.ts +3 -3
  537. package/src/engine/physics/fluid/solver/v3_grid_advect_maccormack_scalar.d.ts.map +1 -1
  538. package/src/engine/physics/fluid/solver/v3_grid_advect_maccormack_scalar.js +7 -0
  539. package/src/engine/physics/fluid/solver/v3_grid_advect_maccormack_velocity.d.ts.map +1 -1
  540. package/src/engine/physics/fluid/solver/v3_grid_advect_maccormack_velocity.js +11 -18
  541. package/src/engine/physics/fluid/solver/v3_grid_advect_sl_velocity.d.ts.map +1 -1
  542. package/src/engine/physics/fluid/solver/v3_grid_advect_sl_velocity.js +7 -0
  543. package/src/engine/physics/fluid/solver/v3_grid_apply_advection_forward.d.ts.map +1 -1
  544. package/src/engine/physics/fluid/solver/v3_grid_apply_advection_forward.js +15 -57
  545. package/src/engine/physics/fluid/solver/v3_grid_apply_scalar_advection.d.ts.map +1 -1
  546. package/src/engine/physics/fluid/solver/v3_grid_apply_scalar_advection.js +7 -0
  547. package/src/engine/physics/fluid/solver/v3_grid_limit8_at_indices.d.ts +28 -0
  548. package/src/engine/physics/fluid/solver/v3_grid_limit8_at_indices.d.ts.map +1 -0
  549. package/src/engine/physics/fluid/solver/v3_grid_limit8_at_indices.js +38 -0
  550. package/src/engine/physics/fluid/solver/v3_mac_advect_maccormack_velocity.d.ts.map +1 -1
  551. package/src/engine/physics/fluid/solver/v3_mac_advect_maccormack_velocity.js +12 -14
  552. package/src/engine/physics/fluid/solver/v3_mac_apply_vorticity_confinement.d.ts.map +1 -1
  553. package/src/engine/physics/fluid/solver/v3_mac_apply_vorticity_confinement.js +9 -0
  554. package/src/engine/physics/mls-mpm/MLS_MPM.d.ts +13 -1
  555. package/src/engine/physics/mls-mpm/MLS_MPM.d.ts.map +1 -1
  556. package/src/engine/physics/mls-mpm/MLS_MPM.js +15 -4
  557. package/src/engine/physics/narrowphase/box_triangle_contact.d.ts.map +1 -1
  558. package/src/engine/physics/narrowphase/box_triangle_contact.js +9 -0
  559. package/src/engine/physics/narrowphase/capsule_contacts.d.ts.map +1 -1
  560. package/src/engine/physics/narrowphase/capsule_contacts.js +10 -11
  561. package/src/engine/physics/narrowphase/sphere_triangle_contact.d.ts.map +1 -1
  562. package/src/engine/physics/narrowphase/sphere_triangle_contact.js +7 -12
  563. package/src/engine/physics/solver/apply_impulse_to_body.d.ts +36 -0
  564. package/src/engine/physics/solver/apply_impulse_to_body.d.ts.map +1 -0
  565. package/src/engine/physics/solver/apply_impulse_to_body.js +86 -0
  566. package/src/engine/physics/solver/solve_contacts.d.ts.map +1 -1
  567. package/src/engine/physics/solver/solve_contacts.js +1 -53
  568. package/src/engine/plugin/EnginePlugin.d.ts +3 -0
  569. package/src/engine/plugin/EnginePlugin.d.ts.map +1 -1
  570. package/src/engine/plugin/EnginePlugin.js +3 -0
  571. package/src/engine/save/storage/IndexedDBStorage.d.ts +1 -0
  572. package/src/engine/save/storage/IndexedDBStorage.d.ts.map +1 -1
  573. package/src/engine/save/storage/IndexedDBStorage.js +50 -50
  574. package/src/engine/sound/ecs/SoundControllerSystem.d.ts.map +1 -1
  575. package/src/engine/sound/ecs/SoundControllerSystem.js +2 -10
  576. package/src/engine/sound/simulation/AcousticSimulator.d.ts.map +1 -1
  577. package/src/engine/sound/simulation/AcousticSimulator.js +2 -1
  578. package/src/engine/sound/simulation/core/AcousticOccluderIndex.d.ts.map +1 -1
  579. package/src/engine/sound/simulation/core/AcousticOccluderIndex.js +4 -6
  580. package/src/engine/sound/simulation/core/OcclusionSolver.d.ts.map +1 -1
  581. package/src/engine/sound/simulation/core/OcclusionSolver.js +2 -9
  582. package/src/engine/sound/simulation/probe/ProbeVisibilityRecheck.d.ts.map +1 -1
  583. package/src/engine/sound/simulation/probe/ProbeVisibilityRecheck.js +9 -14
  584. package/src/engine/sound/simulation/probe/bakeReverbBands.d.ts.map +1 -1
  585. package/src/engine/sound/simulation/probe/bakeReverbBands.js +2 -6
  586. package/src/engine/sound/simulation/probe/bakeVisibility.d.ts.map +1 -1
  587. package/src/engine/sound/simulation/probe/bakeVisibility.js +4 -34
  588. package/src/engine/sound/simulation/probe/probe_densify_portals.d.ts.map +1 -1
  589. package/src/engine/sound/simulation/probe/probe_densify_portals.js +3 -6
  590. package/src/engine/sound/simulation/probe/probe_edge_key.d.ts +31 -0
  591. package/src/engine/sound/simulation/probe/probe_edge_key.d.ts.map +1 -0
  592. package/src/engine/sound/simulation/probe/probe_edge_key.js +38 -0
  593. package/src/engine/sound/simulation/probe/probe_sdf_grid.d.ts +10 -3
  594. package/src/engine/sound/simulation/probe/probe_sdf_grid.d.ts.map +1 -1
  595. package/src/engine/sound/simulation/probe/probe_sdf_grid.js +10 -22
  596. package/src/engine/sound/simulation/probe/probe_visibility_augment.d.ts.map +1 -1
  597. package/src/engine/sound/simulation/probe/probe_visibility_augment.js +12 -48
  598. package/src/engine/sound/simulation/probe/probe_visibility_bridge_components.d.ts.map +1 -1
  599. package/src/engine/sound/simulation/probe/probe_visibility_bridge_components.js +4 -3
  600. package/src/engine/sound/simulation/render/foaReverbImpulseResponse.d.ts +5 -4
  601. package/src/engine/sound/simulation/render/foaReverbImpulseResponse.d.ts.map +1 -1
  602. package/src/engine/sound/simulation/render/foaReverbImpulseResponse.js +23 -31
  603. package/src/engine/sound/simulation/render/reverbImpulseResponse.d.ts +1 -8
  604. package/src/engine/sound/simulation/render/reverbImpulseResponse.d.ts.map +1 -1
  605. package/src/engine/sound/simulation/render/reverbImpulseResponse.js +21 -31
  606. package/src/engine/sound/simulation/render/reverb_band_tails.d.ts +55 -0
  607. package/src/engine/sound/simulation/render/reverb_band_tails.d.ts.map +1 -0
  608. package/src/engine/sound/simulation/render/reverb_band_tails.js +93 -0
  609. package/src/engine/sound/sopra/definition/EventDescriptionSerializationAdapter.d.ts.map +1 -1
  610. package/src/engine/sound/sopra/definition/EventDescriptionSerializationAdapter.js +6 -22
  611. package/src/engine/sound/sopra/definition/clip/BlendContainerAudioClip.d.ts.map +1 -1
  612. package/src/engine/sound/sopra/definition/clip/BlendContainerAudioClip.js +7 -3
  613. package/src/engine/sound/sopra/definition/clip/BlendContainerAudioClipSerializationAdapter.d.ts.map +1 -1
  614. package/src/engine/sound/sopra/definition/clip/BlendContainerAudioClipSerializationAdapter.js +7 -22
  615. package/src/engine/sound/sopra/definition/clip/ContainerAudioClip.d.ts.map +1 -1
  616. package/src/engine/sound/sopra/definition/clip/ContainerAudioClip.js +28 -0
  617. package/src/engine/sound/sopra/definition/clip/RandomContainerAudioClip.d.ts.map +1 -1
  618. package/src/engine/sound/sopra/definition/clip/RandomContainerAudioClip.js +1 -5
  619. package/src/engine/sound/sopra/definition/clip/SequenceContainerAudioClip.d.ts.map +1 -1
  620. package/src/engine/sound/sopra/definition/clip/SequenceContainerAudioClip.js +1 -13
  621. package/src/engine/sound/sopra/definition/clip/SwitchContainerAudioClip.d.ts.map +1 -1
  622. package/src/engine/sound/sopra/definition/clip/SwitchContainerAudioClip.js +2 -8
  623. package/src/engine/sound/sopra/definition/effect/ReverbEffect.d.ts +3 -0
  624. package/src/engine/sound/sopra/definition/effect/ReverbEffect.d.ts.map +1 -1
  625. package/src/engine/sound/sopra/definition/effect/ReverbEffect.js +26 -14
  626. package/src/engine/sound/sopra/runtime/BusGraph.d.ts.map +1 -1
  627. package/src/engine/sound/sopra/runtime/BusGraph.js +21 -33
  628. package/src/engine/sound/sopra/runtime/EventInstance.js +1 -1
  629. package/src/engine/ui/apply_pointer_tilt_3d.d.ts +25 -0
  630. package/src/engine/ui/apply_pointer_tilt_3d.d.ts.map +1 -0
  631. package/src/engine/ui/apply_pointer_tilt_3d.js +50 -0
  632. package/src/engine/ui/bindings/processors/DomElementProcessorTilt3D.d.ts.map +1 -1
  633. package/src/engine/ui/bindings/processors/DomElementProcessorTilt3D.js +9 -26
  634. package/src/generation/filtering/numeric/CellFilterCache.d.ts.map +1 -1
  635. package/src/generation/filtering/numeric/CellFilterCache.js +4 -21
  636. package/src/generation/filtering/numeric/complex/CellFilterSobel.d.ts.map +1 -1
  637. package/src/generation/filtering/numeric/complex/CellFilterSobel.js +11 -20
  638. package/src/generation/filtering/numeric/math/CellFilterPower.d.ts +1 -1
  639. package/src/generation/filtering/numeric/math/CellFilterPower.d.ts.map +1 -1
  640. package/src/generation/filtering/numeric/math/CellFilterPower.js +1 -5
  641. package/src/generation/filtering/numeric/process/computeCellFilterGradient.d.ts.map +1 -1
  642. package/src/generation/filtering/numeric/process/computeCellFilterGradient.js +13 -15
  643. package/src/generation/filtering/numeric/process/computeCellFilterSobel.d.ts +20 -0
  644. package/src/generation/filtering/numeric/process/computeCellFilterSobel.d.ts.map +1 -0
  645. package/src/generation/filtering/numeric/process/computeCellFilterSobel.js +36 -0
  646. package/src/generation/filtering/numeric/util/CellFilterDisplaced.d.ts.map +1 -1
  647. package/src/generation/filtering/numeric/util/CellFilterDisplaced.js +11 -6
  648. package/src/generation/filtering/numeric/util/populateSampler2DFromCellFilter.d.ts.map +1 -1
  649. package/src/generation/filtering/numeric/util/populateSampler2DFromCellFilter.js +18 -36
  650. package/src/generation/filtering/numeric/util/write_cell_filter_to_sampler2d.d.ts +21 -0
  651. package/src/generation/filtering/numeric/util/write_cell_filter_to_sampler2d.d.ts.map +1 -0
  652. package/src/generation/filtering/numeric/util/write_cell_filter_to_sampler2d.js +47 -0
  653. package/src/generation/filtering/util/cell_filter_weights_pick.d.ts +25 -0
  654. package/src/generation/filtering/util/cell_filter_weights_pick.d.ts.map +1 -0
  655. package/src/generation/filtering/util/cell_filter_weights_pick.js +54 -0
  656. package/src/generation/grid/generation/discrete/GridTaskConnectRooms.d.ts.map +1 -1
  657. package/src/generation/grid/generation/discrete/GridTaskConnectRooms.js +14 -1
  658. package/src/generation/markers/GridActionRuleSet.d.ts.map +1 -1
  659. package/src/generation/markers/GridActionRuleSet.js +10 -7
  660. package/src/generation/markers/GridCellActionPlaceMarker.d.ts.map +1 -1
  661. package/src/generation/markers/GridCellActionPlaceMarker.js +10 -12
  662. package/src/generation/markers/actions/probability/MarkerNodeActionSelectWeighted.d.ts.map +1 -1
  663. package/src/generation/markers/actions/probability/MarkerNodeActionSelectWeighted.js +7 -42
  664. package/src/generation/markers/transform/MarkerNodeTransformerOffsetPosition.d.ts.map +1 -1
  665. package/src/generation/markers/transform/MarkerNodeTransformerOffsetPosition.js +11 -6
  666. package/src/generation/placement/GridCellPlacementRule.d.ts.map +1 -1
  667. package/src/generation/placement/GridCellPlacementRule.js +10 -7
  668. package/src/generation/placement/action/GridCellActionPlaceTags.d.ts.map +1 -1
  669. package/src/generation/placement/action/GridCellActionPlaceTags.js +10 -4
  670. package/src/generation/placement/action/random/weighted/CellActionSelectWeightedRandom.d.ts.map +1 -1
  671. package/src/generation/placement/action/random/weighted/CellActionSelectWeightedRandom.js +2 -38
  672. package/src/generation/placement/action/util/GridCellDisplacedAction.d.ts.map +1 -1
  673. package/src/generation/placement/action/util/GridCellDisplacedAction.js +11 -6
  674. package/src/generation/rules/cell/CellMatcherGridPattern.d.ts.map +1 -1
  675. package/src/generation/rules/cell/CellMatcherGridPattern.js +11 -4
  676. package/src/generation/theme/ThemeEngine.d.ts.map +1 -1
  677. package/src/generation/theme/ThemeEngine.js +7 -12
  678. package/src/view/elements/Group.d.ts +24 -3
  679. package/src/view/elements/Group.d.ts.map +1 -1
  680. package/src/view/elements/Group.js +56 -34
@@ -1,728 +1,703 @@
1
- import { assert } from "../../../../../../assert.js";
2
- import { Uint32Heap } from "../../../../../../collection/heap/Uint32Heap.js";
3
- import { NULL_POINTER } from "../BinaryTopology.js";
4
- import { bt_face_get_edge_adjacent_faces } from "./bt_face_get_edge_adjacent_faces.js";
5
- import {
6
- bt_mesh_compute_edge_distance_eikonal
7
- } from "./bt_mesh_compute_edge_distance_eikonal.js";
8
-
9
- /**
10
- * Note that we limit the supported number of neighbors, a reasonable mesh will fit this criteria
11
- * @type {Uint32Array}
12
- */
13
- const neighbors = new Uint32Array(256);
14
-
15
- /**
16
- * Geodesic distance (along the surface) from the goal face to every reached vertex. Rebuilt per
17
- * query; reused to avoid re-allocation.
18
- * @type {Map<number, number>}
19
- */
20
- const geodesic_distance = new Map();
21
-
22
- /**
23
- * Vertices whose geodesic distance has been finalised by the Fast Marching pass.
24
- * @type {Set<number>}
25
- */
26
- const frozen_vertices = new Set();
27
-
28
- /**
29
- * Faces already settled by the corridor trace / fallback search. Reused across passes.
30
- * @type {Set<number>}
31
- */
32
- const visited_faces = new Set();
33
-
34
- /**
35
- * Priority queue, reused by the Fast Marching fill (ordered by f = distance + heuristic) and by the
36
- * fallback corridor search (ordered by distance to goal).
37
- * @type {Uint32Heap}
38
- */
39
- const open = new Uint32Heap();
40
-
41
- /**
42
- * Reconstruction parent pointers for the fallback search: face -> the face we reached it from.
43
- * @type {Map<number, number>}
44
- */
45
- const came_from = new Map();
46
-
47
- const scratch_face_vertices = [0, 0, 0];
48
-
49
- // scratch vertex position used by the Fast Marching pass (kept apart from the trace's pos_* scratch)
50
- const scratch_vertex_pos = new Float64Array(3);
51
-
52
- // the three vertex positions of the face currently being traced
53
- const pos_a = new Float64Array(3);
54
- const pos_b = new Float64Array(3);
55
- const pos_c = new Float64Array(3);
56
-
57
- // in-plane orthonormal basis of the current face (origin at vertex A)
58
- const basis_u = new Float64Array(3);
59
- const basis_v = new Float64Array(3);
60
-
61
- // the goal-distance field value at each vertex of the current face
62
- const scratch_field = new Float64Array(3);
63
-
64
- const scratch_centroid = new Float64Array(3);
65
-
66
- // communicates the ray parameter of the chosen exit edge out of pick_exit_edge (avoids allocation)
67
- let last_exit_t = 0;
68
-
69
- // number of vertices the last Fast Marching pass froze; bounds the corridor trace's iteration count
70
- let frozen_count = 0;
71
-
72
- /**
73
- * Read the three vertex IDs of a triangular face into `out` (length 3). Faces are assumed triangular.
74
- *
75
- * @param {number[]} out
76
- * @param {BinaryTopology} topology
77
- * @param {number} face
78
- * @returns {boolean} false if the face has no loop (uninitialised / out of bounds)
79
- */
80
- function face_read_vertices(out, topology, face) {
81
- const loop_a = topology.face_read_loop(face);
82
-
83
- if (loop_a === NULL_POINTER) {
84
- return false;
85
- }
86
-
87
- const loop_b = topology.loop_read_next(loop_a);
88
- const loop_c = topology.loop_read_next(loop_b);
89
-
90
- out[0] = topology.loop_read_vertex(loop_a);
91
- out[1] = topology.loop_read_vertex(loop_b);
92
- out[2] = topology.loop_read_vertex(loop_c);
93
-
94
- return true;
95
- }
96
-
97
- /**
98
- * Straight-line distance between two vertices.
99
- */
100
- function vertex_distance(topology, a, b) {
101
- topology.vertex_read_coordinate(scratch_vertex_pos, 0, a);
102
- const ax = scratch_vertex_pos[0], ay = scratch_vertex_pos[1], az = scratch_vertex_pos[2];
103
-
104
- topology.vertex_read_coordinate(scratch_vertex_pos, 0, b);
105
- const dx = ax - scratch_vertex_pos[0], dy = ay - scratch_vertex_pos[1], dz = az - scratch_vertex_pos[2];
106
-
107
- return Math.sqrt(dx * dx + dy * dy + dz * dz);
108
- }
109
-
110
- /**
111
- * Straight-line distance from a vertex to a point. Admissible/consistent heuristic for the geodesic
112
- * (true geodesic distance is never shorter than the straight line), used to make the fill goal-directed.
113
- */
114
- function vertex_distance_to_point(topology, vertex, px, py, pz) {
115
- topology.vertex_read_coordinate(scratch_vertex_pos, 0, vertex);
116
- const dx = scratch_vertex_pos[0] - px, dy = scratch_vertex_pos[1] - py, dz = scratch_vertex_pos[2] - pz;
117
- return Math.sqrt(dx * dx + dy * dy + dz * dz);
118
- }
119
-
120
- /**
121
- * The vertex of `face` that is neither `a` nor `b`, or -1 if the face does not have one.
122
- */
123
- function third_vertex(topology, face, a, b) {
124
- let loop = topology.face_read_loop(face);
125
-
126
- for (let i = 0; i < 3; i++) {
127
- const v = topology.loop_read_vertex(loop);
128
-
129
- if (v !== a && v !== b) {
130
- return v;
131
- }
132
-
133
- loop = topology.loop_read_next(loop);
134
- }
135
-
136
- return -1;
137
- }
138
-
139
- /**
140
- * Solve the geodesic distance field outward from the goal face using Fast Marching (the exact Eikonal
141
- * triangle update). The field is goal-directed: the heap is ordered by `distance + h`, where `h` is the
142
- * straight-line distance to the start centroid, so the wavefront sweeps toward the start (exploring the
143
- * start<->goal "ellipse" rather than the whole component). With `early_terminate` it stops the instant
144
- * all three start-face vertices are finalised -- by then every vertex on the geodesic corridor (all of
145
- * which are nearer the goal, hence finalised earlier) is resolved.
146
- *
147
- * The heap uses lazy deletion (stale entries are skipped on pop) instead of an O(n) decrease-key.
148
- *
149
- * @param {BinaryTopology} topology
150
- * @param {number} goal_face seed of the wavefront (distance 0)
151
- * @param {number} start_face heuristic target / early-termination target
152
- * @param {boolean} early_terminate
153
- * @returns {boolean} true if the start face was reached (a path exists), false otherwise
154
- */
155
- function solve_geodesic_field(topology, goal_face, start_face, early_terminate) {
156
- geodesic_distance.clear();
157
- frozen_vertices.clear();
158
- open.clear();
159
-
160
- // start-face vertices (early-termination target) and centroid (heuristic target)
161
- face_read_vertices(scratch_face_vertices, topology, start_face);
162
- const start_v0 = scratch_face_vertices[0];
163
- const start_v1 = scratch_face_vertices[1];
164
- const start_v2 = scratch_face_vertices[2];
165
-
166
- topology.vertex_read_coordinate(pos_a, 0, start_v0);
167
- topology.vertex_read_coordinate(pos_b, 0, start_v1);
168
- topology.vertex_read_coordinate(pos_c, 0, start_v2);
169
- const start_x = (pos_a[0] + pos_b[0] + pos_c[0]) / 3;
170
- const start_y = (pos_a[1] + pos_b[1] + pos_c[1]) / 3;
171
- const start_z = (pos_a[2] + pos_b[2] + pos_c[2]) / 3;
172
-
173
- // seed the goal-face vertices at distance 0
174
- face_read_vertices(scratch_face_vertices, topology, goal_face);
175
- for (let i = 0; i < 3; i++) {
176
- const gv = scratch_face_vertices[i];
177
- if (!geodesic_distance.has(gv)) {
178
- geodesic_distance.set(gv, 0);
179
- open.insert(gv, vertex_distance_to_point(topology, gv, start_x, start_y, start_z));
180
- }
181
- }
182
-
183
- frozen_count = 0;
184
-
185
- while (!open.is_empty()) {
186
- const v = open.pop_min();
187
-
188
- if (frozen_vertices.has(v)) {
189
- continue; // stale heap entry (lazy deletion)
190
- }
191
-
192
- frozen_vertices.add(v);
193
- frozen_count++;
194
-
195
- if (early_terminate
196
- && (v === start_v0 || v === start_v1 || v === start_v2)
197
- && frozen_vertices.has(start_v0) && frozen_vertices.has(start_v1) && frozen_vertices.has(start_v2)
198
- ) {
199
- return true; // start face fully resolved; the whole corridor is finalised
200
- }
201
-
202
- const g_v = geodesic_distance.get(v);
203
-
204
- // walk v's disk cycle (every edge incident to v)
205
- const edge_first = topology.vertex_read_edge(v);
206
- if (edge_first === NULL_POINTER) {
207
- continue;
208
- }
209
-
210
- let edge = edge_first;
211
- do {
212
- const ev1 = topology.edge_read_vertex1(edge);
213
- const ev2 = topology.edge_read_vertex2(edge);
214
- const v_is_first = ev1 === v;
215
- const w = v_is_first ? ev2 : ev1;
216
-
217
- if (frozen_vertices.has(w)) {
218
- // both ends of this edge are finalised -> propagate the wavefront across each incident
219
- // triangle to its third vertex with the exact Eikonal update
220
- const g_w = geodesic_distance.get(w);
221
-
222
- const loop_first = topology.edge_read_loop(edge);
223
- if (loop_first !== NULL_POINTER) {
224
- let loop = loop_first;
225
- do {
226
- const face = topology.loop_read_face(loop);
227
- const t = third_vertex(topology, face, ev1, ev2);
228
-
229
- if (t !== -1 && !frozen_vertices.has(t)) {
230
- const d = bt_mesh_compute_edge_distance_eikonal(topology, v, w, g_v, g_w, t);
231
- const existing = geodesic_distance.get(t);
232
-
233
- if (existing === undefined || d < existing) {
234
- geodesic_distance.set(t, d);
235
- open.insert(t, d + vertex_distance_to_point(topology, t, start_x, start_y, start_z));
236
- }
237
- }
238
-
239
- loop = topology.loop_read_radial_next(loop);
240
- } while (loop !== loop_first && loop !== NULL_POINTER);
241
- }
242
- } else {
243
- // graph-distance relaxation, so w gets a value even before a finalised front forms across
244
- // it; a later Eikonal update lowers it to the true geodesic before w itself is frozen
245
- const d = g_v + vertex_distance(topology, v, w);
246
- const existing = geodesic_distance.get(w);
247
-
248
- if (existing === undefined || d < existing) {
249
- geodesic_distance.set(w, d);
250
- open.insert(w, d + vertex_distance_to_point(topology, w, start_x, start_y, start_z));
251
- }
252
- }
253
-
254
- edge = v_is_first ? topology.edge_read_v1_disk_next(edge) : topology.edge_read_v2_disk_next(edge);
255
- } while (edge !== edge_first && edge !== NULL_POINTER);
256
- }
257
-
258
- // the heap drained: if we were early-terminating, the start was never reached. Either way, report
259
- // whether the start face's vertices ended up finalised (a full solve still answers reachability).
260
- return frozen_vertices.has(start_v0) && frozen_vertices.has(start_v1) && frozen_vertices.has(start_v2);
261
- }
262
-
263
- /**
264
- * The face on the far side of `loop`'s edge, or {@link NULL_POINTER} if the edge is a boundary.
265
- */
266
- function loop_opposite_face(topology, loop, face) {
267
- let radial = topology.loop_read_radial_next(loop);
268
-
269
- while (radial !== loop) {
270
- const f = topology.loop_read_face(radial);
271
-
272
- if (f !== face) {
273
- return f;
274
- }
275
-
276
- radial = topology.loop_read_radial_next(radial);
277
- }
278
-
279
- return NULL_POINTER;
280
- }
281
-
282
- /**
283
- * Average of a face's three vertices' distances to the goal. The average (rather than the minimum)
284
- * keeps a single near-goal vertex of an oversized triangle from making the whole face look close.
285
- * Returns Infinity if any vertex was not reached by the fill.
286
- */
287
- function face_geodesic_distance(topology, face) {
288
- if (!face_read_vertices(scratch_face_vertices, topology, face)) {
289
- return Infinity;
290
- }
291
-
292
- const da = geodesic_distance.get(scratch_face_vertices[0]);
293
- const db = geodesic_distance.get(scratch_face_vertices[1]);
294
- const dc = geodesic_distance.get(scratch_face_vertices[2]);
295
-
296
- if (da === undefined || db === undefined || dc === undefined) {
297
- return Infinity;
298
- }
299
-
300
- return (da + db + dc) / 3;
301
- }
302
-
303
- /**
304
- * Load the geometry of `face` into module scratch: the three vertex positions, the goal-distance field
305
- * value at each vertex, and an in-plane orthonormal basis (origin at vertex A, x-axis along A->B).
306
- */
307
- function load_face_geometry(topology, face) {
308
- face_read_vertices(scratch_face_vertices, topology, face);
309
-
310
- topology.vertex_read_coordinate(pos_a, 0, scratch_face_vertices[0]);
311
- topology.vertex_read_coordinate(pos_b, 0, scratch_face_vertices[1]);
312
- topology.vertex_read_coordinate(pos_c, 0, scratch_face_vertices[2]);
313
-
314
- scratch_field[0] = geodesic_distance.get(scratch_face_vertices[0]);
315
- scratch_field[1] = geodesic_distance.get(scratch_face_vertices[1]);
316
- scratch_field[2] = geodesic_distance.get(scratch_face_vertices[2]);
317
-
318
- const e1x = pos_b[0] - pos_a[0], e1y = pos_b[1] - pos_a[1], e1z = pos_b[2] - pos_a[2];
319
- const e2x = pos_c[0] - pos_a[0], e2y = pos_c[1] - pos_a[1], e2z = pos_c[2] - pos_a[2];
320
-
321
- const e1_len = Math.hypot(e1x, e1y, e1z);
322
- basis_u[0] = e1x / e1_len;
323
- basis_u[1] = e1y / e1_len;
324
- basis_u[2] = e1z / e1_len;
325
-
326
- // n = e1 x e2 (face normal); v = normalize(n x u) completes the in-plane right-handed basis
327
- const nx = e1y * e2z - e1z * e2y;
328
- const ny = e1z * e2x - e1x * e2z;
329
- const nz = e1x * e2y - e1y * e2x;
330
-
331
- const vx = ny * basis_u[2] - nz * basis_u[1];
332
- const vy = nz * basis_u[0] - nx * basis_u[2];
333
- const vz = nx * basis_u[1] - ny * basis_u[0];
334
- const v_len = Math.hypot(vx, vy, vz);
335
- basis_v[0] = vx / v_len;
336
- basis_v[1] = vy / v_len;
337
- basis_v[2] = vz / v_len;
338
- }
339
-
340
- /** Project a world point onto the current face basis x-axis (origin = vertex A). */
341
- function project_x(px, py, pz) {
342
- return (px - pos_a[0]) * basis_u[0] + (py - pos_a[1]) * basis_u[1] + (pz - pos_a[2]) * basis_u[2];
343
- }
344
-
345
- /** Project a world point onto the current face basis y-axis (origin = vertex A). */
346
- function project_y(px, py, pz) {
347
- return (px - pos_a[0]) * basis_v[0] + (py - pos_a[1]) * basis_v[1] + (pz - pos_a[2]) * basis_v[2];
348
- }
349
-
350
- /**
351
- * Parameter `t` at which ray (ox,oy)+t*(dx,dy) crosses segment (e0)->(e1) within the segment, or
352
- * Infinity if it does not cross ahead of the origin.
353
- */
354
- function ray_segment_t(ox, oy, dx, dy, e0x, e0y, e1x, e1y) {
355
- const sx = e1x - e0x;
356
- const sy = e1y - e0y;
357
-
358
- const denom = dx * sy - dy * sx;
359
-
360
- if (denom === 0) {
361
- return Infinity; // parallel
362
- }
363
-
364
- const wx = e0x - ox;
365
- const wy = e0y - oy;
366
-
367
- const t = (wx * sy - wy * sx) / denom; // distance along the ray
368
- const u = (wx * dy - wy * dx) / denom; // position along the segment
369
-
370
- if (t > 1e-9 && u >= -1e-9 && u <= 1 + 1e-9) {
371
- return t;
372
- }
373
-
374
- return Infinity;
375
- }
376
-
377
- /**
378
- * Among the three edges of a triangle (in the face's 2D basis), find the one the ray (ox,oy)+t*(dx,dy)
379
- * exits through (smallest t > 0), and return the face on its far side. Stores the chosen t in
380
- * {@link last_exit_t}.
381
- */
382
- function pick_exit_edge(
383
- ox, oy, dx, dy,
384
- bx, cx, cy,
385
- topology, face, loop_ab, loop_bc, loop_ca
386
- ) {
387
- let best_t = Infinity;
388
- let best_face = NULL_POINTER;
389
-
390
- let t = ray_segment_t(ox, oy, dx, dy, 0, 0, bx, 0); // edge AB
391
- if (t < best_t) { best_t = t; best_face = loop_opposite_face(topology, loop_ab, face); }
392
-
393
- t = ray_segment_t(ox, oy, dx, dy, bx, 0, cx, cy); // edge BC
394
- if (t < best_t) { best_t = t; best_face = loop_opposite_face(topology, loop_bc, face); }
395
-
396
- t = ray_segment_t(ox, oy, dx, dy, cx, cy, 0, 0); // edge CA
397
- if (t < best_t) { best_t = t; best_face = loop_opposite_face(topology, loop_ca, face); }
398
-
399
- last_exit_t = best_t;
400
- return best_face;
401
- }
402
-
403
- /**
404
- * The unvisited neighbour face with the smallest average goal-distance. Used as a robust fallback when
405
- * the gradient march cannot pick a forward exit edge.
406
- */
407
- function lowest_unvisited_neighbour(topology, face) {
408
- const count = bt_face_get_edge_adjacent_faces(neighbors, 0, topology, face);
409
-
410
- let best_face = NULL_POINTER;
411
- let best_distance = Infinity;
412
-
413
- for (let i = 0; i < count; i++) {
414
- const neighbor = neighbors[i];
415
-
416
- if (visited_faces.has(neighbor)) {
417
- continue;
418
- }
419
-
420
- const distance = face_geodesic_distance(topology, neighbor);
421
-
422
- if (distance < best_distance) {
423
- best_distance = distance;
424
- best_face = neighbor;
425
- }
426
- }
427
-
428
- return best_face;
429
- }
430
-
431
- /** Write the centroid of a triangular face into {@link scratch_centroid}. */
432
- function face_centroid(topology, face) {
433
- face_read_vertices(scratch_face_vertices, topology, face);
434
-
435
- topology.vertex_read_coordinate(pos_a, 0, scratch_face_vertices[0]);
436
- topology.vertex_read_coordinate(pos_b, 0, scratch_face_vertices[1]);
437
- topology.vertex_read_coordinate(pos_c, 0, scratch_face_vertices[2]);
438
-
439
- scratch_centroid[0] = (pos_a[0] + pos_b[0] + pos_c[0]) / 3;
440
- scratch_centroid[1] = (pos_a[1] + pos_b[1] + pos_c[1]) / 3;
441
- scratch_centroid[2] = (pos_a[2] + pos_b[2] + pos_c[2]) / 3;
442
- }
443
-
444
- /**
445
- * Trace the steepest descent of the geodesic field from the start face to the goal face, writing the
446
- * face corridor into `output` in START -> GOAL order. Following the in-plane gradient downhill crosses
447
- * exactly the faces the true geodesic does, so the corridor admits the shortest path. Returns 0 if the
448
- * descent cannot complete (e.g. it has to wind around a hole, where the field's gradient is no longer
449
- * a reliable guide) -- the caller then falls back to a graph search.
450
- *
451
- * @returns {number} faces written, or 0 on failure / if the corridor exceeds `max_path_length`
452
- */
453
- function trace_corridor(output, topology, start_face, goal_face, max_path_length) {
454
- visited_faces.clear();
455
-
456
- face_centroid(topology, start_face);
457
- let world_x = scratch_centroid[0];
458
- let world_y = scratch_centroid[1];
459
- let world_z = scratch_centroid[2];
460
-
461
- let current = start_face;
462
- let previous = NULL_POINTER;
463
- let length = 0;
464
-
465
- // each step adds a fresh face; the count of finalised vertices bounds the reachable faces
466
- const iteration_cap = frozen_count * 2 + 8;
467
-
468
- for (let iteration = 0; iteration < iteration_cap; iteration++) {
469
- if (length >= max_path_length) {
470
- return 0;
471
- }
472
-
473
- output[length] = current;
474
- length++;
475
-
476
- if (current === goal_face) {
477
- return length;
478
- }
479
-
480
- visited_faces.add(current);
481
-
482
- load_face_geometry(topology, current);
483
-
484
- const ga = scratch_field[0], gb = scratch_field[1], gc = scratch_field[2];
485
-
486
- const px = project_x(world_x, world_y, world_z);
487
- const py = project_y(world_x, world_y, world_z);
488
-
489
- const bx = project_x(pos_b[0], pos_b[1], pos_b[2]); // B.y == 0 by construction
490
- const cx = project_x(pos_c[0], pos_c[1], pos_c[2]);
491
- const cy = project_y(pos_c[0], pos_c[1], pos_c[2]);
492
-
493
- // in-plane gradient of the (linear) field; descend it (negative gradient points at the goal)
494
- const grad_x = (gb - ga) / bx;
495
- const grad_y = (gc - ga - grad_x * cx) / cy;
496
-
497
- let dir_x = -grad_x;
498
- let dir_y = -grad_y;
499
- const dir_len = Math.hypot(dir_x, dir_y);
500
-
501
- let next = NULL_POINTER;
502
-
503
- if (dir_len > 1e-12) {
504
- dir_x /= dir_len;
505
- dir_y /= dir_len;
506
-
507
- const loop_a = topology.face_read_loop(current);
508
- const loop_b = topology.loop_read_next(loop_a);
509
- const loop_c = topology.loop_read_next(loop_b);
510
-
511
- next = pick_exit_edge(px, py, dir_x, dir_y, bx, cx, cy, topology, current, loop_a, loop_b, loop_c);
512
-
513
- if (next !== NULL_POINTER && next !== previous && !visited_faces.has(next)) {
514
- // follow the gradient: re-enter the next face at the exit point (on the shared edge)
515
- const exit_x = px + dir_x * last_exit_t;
516
- const exit_y = py + dir_y * last_exit_t;
517
-
518
- world_x = pos_a[0] + exit_x * basis_u[0] + exit_y * basis_v[0];
519
- world_y = pos_a[1] + exit_x * basis_u[1] + exit_y * basis_v[1];
520
- world_z = pos_a[2] + exit_x * basis_u[2] + exit_y * basis_v[2];
521
-
522
- previous = current;
523
- current = next;
524
- continue;
525
- }
526
- }
527
-
528
- // the gradient gave no usable forward edge (a boundary/hole, a vertex hit, or it pointed into
529
- // an already-traced face). Fall back to the lowest-field unvisited neighbour, re-entering at
530
- // its centroid. If that happens we are near a hole, so the caller will likely re-run as a
531
- // graph search; here we just keep the corridor connected.
532
- next = lowest_unvisited_neighbour(topology, current);
533
-
534
- if (next === NULL_POINTER) {
535
- return 0;
536
- }
537
-
538
- face_centroid(topology, next);
539
- world_x = scratch_centroid[0];
540
- world_y = scratch_centroid[1];
541
- world_z = scratch_centroid[2];
542
-
543
- previous = current;
544
- current = next;
545
- }
546
-
547
- return 0;
548
- }
549
-
550
- /**
551
- * Walk parent pointers from goal back to start, writing the face sequence into `output` in
552
- * START -> GOAL order, or 0 if the corridor exceeds `max_path_length`.
553
- */
554
- function construct_path(output, start_face, goal_face, max_path_length) {
555
- let length = 0;
556
- let current = goal_face;
557
-
558
- while (true) {
559
- if (length >= max_path_length) {
560
- return 0;
561
- }
562
-
563
- output[length] = current;
564
- length++;
565
-
566
- if (current === start_face) {
567
- break;
568
- }
569
-
570
- current = came_from.get(current);
571
- }
572
-
573
- const half_length = length >> 1;
574
-
575
- for (let i = 0; i < half_length; i++) {
576
- const mirror_i = length - 1 - i;
577
- const temp = output[i];
578
-
579
- output[i] = output[mirror_i];
580
- output[mirror_i] = temp;
581
- }
582
-
583
- return length;
584
- }
585
-
586
- /**
587
- * Fallback corridor finder: a best-first walk over faces, always expanding the one currently closest
588
- * to the goal on the geodesic field. Always finds a corridor when one exists within the resolved field
589
- * (it routes around holes the gradient trace could not), at the cost of not necessarily hugging the
590
- * exact geodesic.
591
- *
592
- * @returns {number} faces written, or 0 if no path / it exceeds `max_path_length`
593
- */
594
- function search_corridor(output, topology, start_face, goal_face, max_path_length) {
595
- open.clear();
596
- came_from.clear();
597
- visited_faces.clear();
598
-
599
- open.insert(start_face, face_geodesic_distance(topology, start_face));
600
- visited_faces.add(start_face);
601
-
602
- while (!open.is_empty()) {
603
- const current = open.pop_min();
604
-
605
- if (current === goal_face) {
606
- return construct_path(output, start_face, goal_face, max_path_length);
607
- }
608
-
609
- const count = bt_face_get_edge_adjacent_faces(neighbors, 0, topology, current);
610
-
611
- for (let i = 0; i < count; i++) {
612
- const neighbor = neighbors[i];
613
-
614
- if (visited_faces.has(neighbor)) {
615
- continue;
616
- }
617
-
618
- const distance = face_geodesic_distance(topology, neighbor);
619
-
620
- if (distance === Infinity) {
621
- continue; // outside the resolved field
622
- }
623
-
624
- visited_faces.add(neighbor);
625
- came_from.set(neighbor, current);
626
- open.insert(neighbor, distance);
627
- }
628
- }
629
-
630
- return 0;
631
- }
632
-
633
- /**
634
- * Extract the corridor from the resolved field: gradient trace first (hugs the geodesic), graph-walk
635
- * fallback for winding around holes.
636
- *
637
- * @returns {number} faces written, or 0 if neither method produced a corridor
638
- */
639
- function extract_corridor(output, topology, start_face, goal_face, max_path_length) {
640
- const traced = trace_corridor(output, topology, start_face, goal_face, max_path_length);
641
-
642
- if (traced !== 0) {
643
- return traced;
644
- }
645
-
646
- return search_corridor(output, topology, start_face, goal_face, max_path_length);
647
- }
648
-
649
- /**
650
- * Find a shortest path through topology faces.
651
- * If a path is found - the result will contain start and goal faces.
652
- *
653
- * The corridor is steered by an exact-geodesic distance field rather than a face-graph metric. A
654
- * goal-directed Fast Marching pass (exact Eikonal triangle update) solves the true along-surface
655
- * distance-to-goal at each vertex, sweeping toward the start and stopping as soon as the start face is
656
- * resolved. Because the field measures the real walked distance (not centroid-to-centroid hops), the
657
- * corridor heads straight at the goal and crosses a finely tessellated patch instead of skirting around
658
- * it. The corridor is then extracted by descending the field's gradient from the start (which hugs the
659
- * geodesic, so the downstream funnel/string-pull recovers the exact shortest path); where the gradient
660
- * is an unreliable guide -- winding around a hole -- a best-first graph walk on the same field takes
661
- * over. A full (non-early-terminated) solve is used as a backstop for the rare case the bounded fill
662
- * left the corridor under-resolved.
663
- *
664
- * NOTE: if either start or goal faces are not part of the topology - an empty path will be produced.
665
- *
666
- * @param {number[]|Uint32Array} output path will be written here as a sequence of face IDs
667
- * @param {number} start_face_id
668
- * @param {number} goal_face_id
669
- * @param {BinaryTopology} topology
670
- * @param {number} [max_path_length=Infinity] cap on the number of faces written to `output`. If the
671
- * shortest corridor is longer than this it does not fit the buffer and 0 is returned. Pass the
672
- * capacity of `output` to guarantee no out-of-bounds writes.
673
- * @returns {number} number of faces that make up the path. This will be 0 if no path is found (or the
674
- * path does not fit `max_path_length`).
675
- */
676
- export function bt_mesh_face_find_path(
677
- output,
678
- start_face_id,
679
- goal_face_id,
680
- topology,
681
- max_path_length = Infinity,
682
- ) {
683
- assert.isArrayLike(output, 'output');
684
- assert.isNonNegativeInteger(start_face_id, 'start_face_id');
685
- assert.isNonNegativeInteger(goal_face_id, 'goal_face_id');
686
- assert.notNull(topology, 'topology');
687
- assert.defined(topology, 'topology');
688
- assert.equal(topology.isBinaryTopology, true, 'topology.isBinaryTopology !== true');
689
-
690
- if (start_face_id === goal_face_id) {
691
- // trivial path; still validate the face actually exists
692
- if (!topology.faces.is_allocated(start_face_id)) {
693
- return 0;
694
- }
695
-
696
- if (max_path_length < 1) {
697
- return 0;
698
- }
699
-
700
- output[0] = start_face_id;
701
- return 1;
702
- }
703
-
704
- // both faces must be part of the topology
705
- if (!topology.faces.is_allocated(start_face_id) || !topology.faces.is_allocated(goal_face_id)) {
706
- return 0;
707
- }
708
-
709
- // Fast path: goal-directed fill bounded to the start<->goal region. When the start is unreachable
710
- // this drains exhaustively (it never early-exits), so a false return is a definitive "no path" --
711
- // no second solve needed.
712
- if (!solve_geodesic_field(topology, goal_face_id, start_face_id, true)) {
713
- return 0; // no path between the two faces
714
- }
715
-
716
- const corridor = extract_corridor(output, topology, start_face_id, goal_face_id, max_path_length);
717
-
718
- if (corridor !== 0) {
719
- return corridor;
720
- }
721
-
722
- // Backstop: the bounded fill resolved enough to reach the start but left the corridor
723
- // under-resolved for the extractor (rare; a corridor that winds far around a hole). A full solve
724
- // resolves the whole reachable component, then the extractor cannot fail for a connected pair.
725
- solve_geodesic_field(topology, goal_face_id, start_face_id, false);
726
-
727
- return extract_corridor(output, topology, start_face_id, goal_face_id, max_path_length);
728
- }
1
+ import { bt_face_read_triangle_vertices } from "./bt_face_read_triangle.js";
2
+ import { v3_distance } from "../../../../../vec3/v3_distance.js";
3
+ import { bt_vertex_distance_to_vertex } from "./bt_vertex_distance_to_vertex.js";
4
+ import { assert } from "../../../../../../assert.js";
5
+ import { Uint32Heap } from "../../../../../../collection/heap/Uint32Heap.js";
6
+ import { NULL_POINTER } from "../BinaryTopology.js";
7
+ import { bt_face_get_edge_adjacent_faces } from "./bt_face_get_edge_adjacent_faces.js";
8
+ import {
9
+ bt_mesh_compute_edge_distance_eikonal
10
+ } from "./bt_mesh_compute_edge_distance_eikonal.js";
11
+
12
+ /**
13
+ * Note that we limit the supported number of neighbors, a reasonable mesh will fit this criteria
14
+ * @type {Uint32Array}
15
+ */
16
+ const neighbors = new Uint32Array(256);
17
+
18
+ /**
19
+ * Geodesic distance (along the surface) from the goal face to every reached vertex. Rebuilt per
20
+ * query; reused to avoid re-allocation.
21
+ * @type {Map<number, number>}
22
+ */
23
+ const geodesic_distance = new Map();
24
+
25
+ /**
26
+ * Vertices whose geodesic distance has been finalised by the Fast Marching pass.
27
+ * @type {Set<number>}
28
+ */
29
+ const frozen_vertices = new Set();
30
+
31
+ /**
32
+ * Faces already settled by the corridor trace / fallback search. Reused across passes.
33
+ * @type {Set<number>}
34
+ */
35
+ const visited_faces = new Set();
36
+
37
+ /**
38
+ * Priority queue, reused by the Fast Marching fill (ordered by f = distance + heuristic) and by the
39
+ * fallback corridor search (ordered by distance to goal).
40
+ * @type {Uint32Heap}
41
+ */
42
+ const open = new Uint32Heap();
43
+
44
+ /**
45
+ * Reconstruction parent pointers for the fallback search: face -> the face we reached it from.
46
+ * @type {Map<number, number>}
47
+ */
48
+ const came_from = new Map();
49
+
50
+ const scratch_face_vertices = [0, 0, 0];
51
+
52
+ // scratch vertex position used by the Fast Marching pass (kept apart from the trace's pos_* scratch)
53
+ const scratch_vertex_pos = new Float64Array(3);
54
+
55
+ // the three vertex positions of the face currently being traced
56
+ const pos_a = new Float64Array(3);
57
+ const pos_b = new Float64Array(3);
58
+ const pos_c = new Float64Array(3);
59
+
60
+ // in-plane orthonormal basis of the current face (origin at vertex A)
61
+ const basis_u = new Float64Array(3);
62
+ const basis_v = new Float64Array(3);
63
+
64
+ // the goal-distance field value at each vertex of the current face
65
+ const scratch_field = new Float64Array(3);
66
+
67
+ const scratch_centroid = new Float64Array(3);
68
+
69
+ // communicates the ray parameter of the chosen exit edge out of pick_exit_edge (avoids allocation)
70
+ let last_exit_t = 0;
71
+
72
+ // number of vertices the last Fast Marching pass froze; bounds the corridor trace's iteration count
73
+ let frozen_count = 0;
74
+
75
+ /**
76
+ * Read the three vertex IDs of a triangular face into `out` (length 3). Faces are assumed triangular.
77
+ *
78
+ * @param {number[]} out
79
+ * @param {BinaryTopology} topology
80
+ * @param {number} face
81
+ * @returns {boolean} false if the face has no loop (uninitialised / out of bounds)
82
+ */
83
+
84
+
85
+
86
+ /**
87
+ * Straight-line distance from a vertex to a point. Admissible/consistent heuristic for the geodesic
88
+ * (true geodesic distance is never shorter than the straight line), used to make the fill goal-directed.
89
+ */
90
+ function vertex_distance_to_point(topology, vertex, px, py, pz) {
91
+ topology.vertex_read_coordinate(scratch_vertex_pos, 0, vertex);
92
+ return v3_distance(scratch_vertex_pos[0], scratch_vertex_pos[1], scratch_vertex_pos[2], px, py, pz);
93
+ }
94
+
95
+ /**
96
+ * The vertex of `face` that is neither `a` nor `b`, or -1 if the face does not have one.
97
+ */
98
+ function third_vertex(topology, face, a, b) {
99
+ let loop = topology.face_read_loop(face);
100
+
101
+ for (let i = 0; i < 3; i++) {
102
+ const v = topology.loop_read_vertex(loop);
103
+
104
+ if (v !== a && v !== b) {
105
+ return v;
106
+ }
107
+
108
+ loop = topology.loop_read_next(loop);
109
+ }
110
+
111
+ return -1;
112
+ }
113
+
114
+ /**
115
+ * Solve the geodesic distance field outward from the goal face using Fast Marching (the exact Eikonal
116
+ * triangle update). The field is goal-directed: the heap is ordered by `distance + h`, where `h` is the
117
+ * straight-line distance to the start centroid, so the wavefront sweeps toward the start (exploring the
118
+ * start<->goal "ellipse" rather than the whole component). With `early_terminate` it stops the instant
119
+ * all three start-face vertices are finalised -- by then every vertex on the geodesic corridor (all of
120
+ * which are nearer the goal, hence finalised earlier) is resolved.
121
+ *
122
+ * The heap uses lazy deletion (stale entries are skipped on pop) instead of an O(n) decrease-key.
123
+ *
124
+ * @param {BinaryTopology} topology
125
+ * @param {number} goal_face seed of the wavefront (distance 0)
126
+ * @param {number} start_face heuristic target / early-termination target
127
+ * @param {boolean} early_terminate
128
+ * @returns {boolean} true if the start face was reached (a path exists), false otherwise
129
+ */
130
+ function solve_geodesic_field(topology, goal_face, start_face, early_terminate) {
131
+ geodesic_distance.clear();
132
+ frozen_vertices.clear();
133
+ open.clear();
134
+
135
+ // start-face vertices (early-termination target) and centroid (heuristic target)
136
+ bt_face_read_triangle_vertices(scratch_face_vertices, topology, start_face);
137
+ const start_v0 = scratch_face_vertices[0];
138
+ const start_v1 = scratch_face_vertices[1];
139
+ const start_v2 = scratch_face_vertices[2];
140
+
141
+ topology.vertex_read_coordinate(pos_a, 0, start_v0);
142
+ topology.vertex_read_coordinate(pos_b, 0, start_v1);
143
+ topology.vertex_read_coordinate(pos_c, 0, start_v2);
144
+ const start_x = (pos_a[0] + pos_b[0] + pos_c[0]) / 3;
145
+ const start_y = (pos_a[1] + pos_b[1] + pos_c[1]) / 3;
146
+ const start_z = (pos_a[2] + pos_b[2] + pos_c[2]) / 3;
147
+
148
+ // seed the goal-face vertices at distance 0
149
+ bt_face_read_triangle_vertices(scratch_face_vertices, topology, goal_face);
150
+ for (let i = 0; i < 3; i++) {
151
+ const gv = scratch_face_vertices[i];
152
+ if (!geodesic_distance.has(gv)) {
153
+ geodesic_distance.set(gv, 0);
154
+ open.insert(gv, vertex_distance_to_point(topology, gv, start_x, start_y, start_z));
155
+ }
156
+ }
157
+
158
+ frozen_count = 0;
159
+
160
+ while (!open.is_empty()) {
161
+ const v = open.pop_min();
162
+
163
+ if (frozen_vertices.has(v)) {
164
+ continue; // stale heap entry (lazy deletion)
165
+ }
166
+
167
+ frozen_vertices.add(v);
168
+ frozen_count++;
169
+
170
+ if (early_terminate
171
+ && (v === start_v0 || v === start_v1 || v === start_v2)
172
+ && frozen_vertices.has(start_v0) && frozen_vertices.has(start_v1) && frozen_vertices.has(start_v2)
173
+ ) {
174
+ return true; // start face fully resolved; the whole corridor is finalised
175
+ }
176
+
177
+ const g_v = geodesic_distance.get(v);
178
+
179
+ // walk v's disk cycle (every edge incident to v)
180
+ const edge_first = topology.vertex_read_edge(v);
181
+ if (edge_first === NULL_POINTER) {
182
+ continue;
183
+ }
184
+
185
+ let edge = edge_first;
186
+ do {
187
+ const ev1 = topology.edge_read_vertex1(edge);
188
+ const ev2 = topology.edge_read_vertex2(edge);
189
+ const v_is_first = ev1 === v;
190
+ const w = v_is_first ? ev2 : ev1;
191
+
192
+ if (frozen_vertices.has(w)) {
193
+ // both ends of this edge are finalised -> propagate the wavefront across each incident
194
+ // triangle to its third vertex with the exact Eikonal update
195
+ const g_w = geodesic_distance.get(w);
196
+
197
+ const loop_first = topology.edge_read_loop(edge);
198
+ if (loop_first !== NULL_POINTER) {
199
+ let loop = loop_first;
200
+ do {
201
+ const face = topology.loop_read_face(loop);
202
+ const t = third_vertex(topology, face, ev1, ev2);
203
+
204
+ if (t !== -1 && !frozen_vertices.has(t)) {
205
+ const d = bt_mesh_compute_edge_distance_eikonal(topology, v, w, g_v, g_w, t);
206
+ const existing = geodesic_distance.get(t);
207
+
208
+ if (existing === undefined || d < existing) {
209
+ geodesic_distance.set(t, d);
210
+ open.insert(t, d + vertex_distance_to_point(topology, t, start_x, start_y, start_z));
211
+ }
212
+ }
213
+
214
+ loop = topology.loop_read_radial_next(loop);
215
+ } while (loop !== loop_first && loop !== NULL_POINTER);
216
+ }
217
+ } else {
218
+ // graph-distance relaxation, so w gets a value even before a finalised front forms across
219
+ // it; a later Eikonal update lowers it to the true geodesic before w itself is frozen
220
+ const d = g_v + bt_vertex_distance_to_vertex(topology, v, w);
221
+ const existing = geodesic_distance.get(w);
222
+
223
+ if (existing === undefined || d < existing) {
224
+ geodesic_distance.set(w, d);
225
+ open.insert(w, d + vertex_distance_to_point(topology, w, start_x, start_y, start_z));
226
+ }
227
+ }
228
+
229
+ edge = v_is_first ? topology.edge_read_v1_disk_next(edge) : topology.edge_read_v2_disk_next(edge);
230
+ } while (edge !== edge_first && edge !== NULL_POINTER);
231
+ }
232
+
233
+ // the heap drained: if we were early-terminating, the start was never reached. Either way, report
234
+ // whether the start face's vertices ended up finalised (a full solve still answers reachability).
235
+ return frozen_vertices.has(start_v0) && frozen_vertices.has(start_v1) && frozen_vertices.has(start_v2);
236
+ }
237
+
238
+ /**
239
+ * The face on the far side of `loop`'s edge, or {@link NULL_POINTER} if the edge is a boundary.
240
+ */
241
+ function loop_opposite_face(topology, loop, face) {
242
+ let radial = topology.loop_read_radial_next(loop);
243
+
244
+ while (radial !== loop) {
245
+ const f = topology.loop_read_face(radial);
246
+
247
+ if (f !== face) {
248
+ return f;
249
+ }
250
+
251
+ radial = topology.loop_read_radial_next(radial);
252
+ }
253
+
254
+ return NULL_POINTER;
255
+ }
256
+
257
+ /**
258
+ * Average of a face's three vertices' distances to the goal. The average (rather than the minimum)
259
+ * keeps a single near-goal vertex of an oversized triangle from making the whole face look close.
260
+ * Returns Infinity if any vertex was not reached by the fill.
261
+ */
262
+ function face_geodesic_distance(topology, face) {
263
+ if (!bt_face_read_triangle_vertices(scratch_face_vertices, topology, face)) {
264
+ return Infinity;
265
+ }
266
+
267
+ const da = geodesic_distance.get(scratch_face_vertices[0]);
268
+ const db = geodesic_distance.get(scratch_face_vertices[1]);
269
+ const dc = geodesic_distance.get(scratch_face_vertices[2]);
270
+
271
+ if (da === undefined || db === undefined || dc === undefined) {
272
+ return Infinity;
273
+ }
274
+
275
+ return (da + db + dc) / 3;
276
+ }
277
+
278
+ /**
279
+ * Load the geometry of `face` into module scratch: the three vertex positions, the goal-distance field
280
+ * value at each vertex, and an in-plane orthonormal basis (origin at vertex A, x-axis along A->B).
281
+ */
282
+ function load_face_geometry(topology, face) {
283
+ bt_face_read_triangle_vertices(scratch_face_vertices, topology, face);
284
+
285
+ topology.vertex_read_coordinate(pos_a, 0, scratch_face_vertices[0]);
286
+ topology.vertex_read_coordinate(pos_b, 0, scratch_face_vertices[1]);
287
+ topology.vertex_read_coordinate(pos_c, 0, scratch_face_vertices[2]);
288
+
289
+ scratch_field[0] = geodesic_distance.get(scratch_face_vertices[0]);
290
+ scratch_field[1] = geodesic_distance.get(scratch_face_vertices[1]);
291
+ scratch_field[2] = geodesic_distance.get(scratch_face_vertices[2]);
292
+
293
+ const e1x = pos_b[0] - pos_a[0], e1y = pos_b[1] - pos_a[1], e1z = pos_b[2] - pos_a[2];
294
+ const e2x = pos_c[0] - pos_a[0], e2y = pos_c[1] - pos_a[1], e2z = pos_c[2] - pos_a[2];
295
+
296
+ const e1_len = Math.hypot(e1x, e1y, e1z);
297
+ basis_u[0] = e1x / e1_len;
298
+ basis_u[1] = e1y / e1_len;
299
+ basis_u[2] = e1z / e1_len;
300
+
301
+ // n = e1 x e2 (face normal); v = normalize(n x u) completes the in-plane right-handed basis
302
+ const nx = e1y * e2z - e1z * e2y;
303
+ const ny = e1z * e2x - e1x * e2z;
304
+ const nz = e1x * e2y - e1y * e2x;
305
+
306
+ const vx = ny * basis_u[2] - nz * basis_u[1];
307
+ const vy = nz * basis_u[0] - nx * basis_u[2];
308
+ const vz = nx * basis_u[1] - ny * basis_u[0];
309
+ const v_len = Math.hypot(vx, vy, vz);
310
+ basis_v[0] = vx / v_len;
311
+ basis_v[1] = vy / v_len;
312
+ basis_v[2] = vz / v_len;
313
+ }
314
+
315
+ /** Project a world point onto the current face basis x-axis (origin = vertex A). */
316
+ function project_x(px, py, pz) {
317
+ return (px - pos_a[0]) * basis_u[0] + (py - pos_a[1]) * basis_u[1] + (pz - pos_a[2]) * basis_u[2];
318
+ }
319
+
320
+ /** Project a world point onto the current face basis y-axis (origin = vertex A). */
321
+ function project_y(px, py, pz) {
322
+ return (px - pos_a[0]) * basis_v[0] + (py - pos_a[1]) * basis_v[1] + (pz - pos_a[2]) * basis_v[2];
323
+ }
324
+
325
+ /**
326
+ * Parameter `t` at which ray (ox,oy)+t*(dx,dy) crosses segment (e0)->(e1) within the segment, or
327
+ * Infinity if it does not cross ahead of the origin.
328
+ */
329
+ function ray_segment_t(ox, oy, dx, dy, e0x, e0y, e1x, e1y) {
330
+ const sx = e1x - e0x;
331
+ const sy = e1y - e0y;
332
+
333
+ const denom = dx * sy - dy * sx;
334
+
335
+ if (denom === 0) {
336
+ return Infinity; // parallel
337
+ }
338
+
339
+ const wx = e0x - ox;
340
+ const wy = e0y - oy;
341
+
342
+ const t = (wx * sy - wy * sx) / denom; // distance along the ray
343
+ const u = (wx * dy - wy * dx) / denom; // position along the segment
344
+
345
+ if (t > 1e-9 && u >= -1e-9 && u <= 1 + 1e-9) {
346
+ return t;
347
+ }
348
+
349
+ return Infinity;
350
+ }
351
+
352
+ /**
353
+ * Among the three edges of a triangle (in the face's 2D basis), find the one the ray (ox,oy)+t*(dx,dy)
354
+ * exits through (smallest t > 0), and return the face on its far side. Stores the chosen t in
355
+ * {@link last_exit_t}.
356
+ */
357
+ function pick_exit_edge(
358
+ ox, oy, dx, dy,
359
+ bx, cx, cy,
360
+ topology, face, loop_ab, loop_bc, loop_ca
361
+ ) {
362
+ let best_t = Infinity;
363
+ let best_face = NULL_POINTER;
364
+
365
+ let t = ray_segment_t(ox, oy, dx, dy, 0, 0, bx, 0); // edge AB
366
+ if (t < best_t) { best_t = t; best_face = loop_opposite_face(topology, loop_ab, face); }
367
+
368
+ t = ray_segment_t(ox, oy, dx, dy, bx, 0, cx, cy); // edge BC
369
+ if (t < best_t) { best_t = t; best_face = loop_opposite_face(topology, loop_bc, face); }
370
+
371
+ t = ray_segment_t(ox, oy, dx, dy, cx, cy, 0, 0); // edge CA
372
+ if (t < best_t) { best_t = t; best_face = loop_opposite_face(topology, loop_ca, face); }
373
+
374
+ last_exit_t = best_t;
375
+ return best_face;
376
+ }
377
+
378
+ /**
379
+ * The unvisited neighbour face with the smallest average goal-distance. Used as a robust fallback when
380
+ * the gradient march cannot pick a forward exit edge.
381
+ */
382
+ function lowest_unvisited_neighbour(topology, face) {
383
+ const count = bt_face_get_edge_adjacent_faces(neighbors, 0, topology, face);
384
+
385
+ let best_face = NULL_POINTER;
386
+ let best_distance = Infinity;
387
+
388
+ for (let i = 0; i < count; i++) {
389
+ const neighbor = neighbors[i];
390
+
391
+ if (visited_faces.has(neighbor)) {
392
+ continue;
393
+ }
394
+
395
+ const distance = face_geodesic_distance(topology, neighbor);
396
+
397
+ if (distance < best_distance) {
398
+ best_distance = distance;
399
+ best_face = neighbor;
400
+ }
401
+ }
402
+
403
+ return best_face;
404
+ }
405
+
406
+ /** Write the centroid of a triangular face into {@link scratch_centroid}. */
407
+ function face_centroid(topology, face) {
408
+ bt_face_read_triangle_vertices(scratch_face_vertices, topology, face);
409
+
410
+ topology.vertex_read_coordinate(pos_a, 0, scratch_face_vertices[0]);
411
+ topology.vertex_read_coordinate(pos_b, 0, scratch_face_vertices[1]);
412
+ topology.vertex_read_coordinate(pos_c, 0, scratch_face_vertices[2]);
413
+
414
+ scratch_centroid[0] = (pos_a[0] + pos_b[0] + pos_c[0]) / 3;
415
+ scratch_centroid[1] = (pos_a[1] + pos_b[1] + pos_c[1]) / 3;
416
+ scratch_centroid[2] = (pos_a[2] + pos_b[2] + pos_c[2]) / 3;
417
+ }
418
+
419
+ /**
420
+ * Trace the steepest descent of the geodesic field from the start face to the goal face, writing the
421
+ * face corridor into `output` in START -> GOAL order. Following the in-plane gradient downhill crosses
422
+ * exactly the faces the true geodesic does, so the corridor admits the shortest path. Returns 0 if the
423
+ * descent cannot complete (e.g. it has to wind around a hole, where the field's gradient is no longer
424
+ * a reliable guide) -- the caller then falls back to a graph search.
425
+ *
426
+ * @returns {number} faces written, or 0 on failure / if the corridor exceeds `max_path_length`
427
+ */
428
+ function trace_corridor(output, topology, start_face, goal_face, max_path_length) {
429
+ visited_faces.clear();
430
+
431
+ face_centroid(topology, start_face);
432
+ let world_x = scratch_centroid[0];
433
+ let world_y = scratch_centroid[1];
434
+ let world_z = scratch_centroid[2];
435
+
436
+ let current = start_face;
437
+ let previous = NULL_POINTER;
438
+ let length = 0;
439
+
440
+ // each step adds a fresh face; the count of finalised vertices bounds the reachable faces
441
+ const iteration_cap = frozen_count * 2 + 8;
442
+
443
+ for (let iteration = 0; iteration < iteration_cap; iteration++) {
444
+ if (length >= max_path_length) {
445
+ return 0;
446
+ }
447
+
448
+ output[length] = current;
449
+ length++;
450
+
451
+ if (current === goal_face) {
452
+ return length;
453
+ }
454
+
455
+ visited_faces.add(current);
456
+
457
+ load_face_geometry(topology, current);
458
+
459
+ const ga = scratch_field[0], gb = scratch_field[1], gc = scratch_field[2];
460
+
461
+ const px = project_x(world_x, world_y, world_z);
462
+ const py = project_y(world_x, world_y, world_z);
463
+
464
+ const bx = project_x(pos_b[0], pos_b[1], pos_b[2]); // B.y == 0 by construction
465
+ const cx = project_x(pos_c[0], pos_c[1], pos_c[2]);
466
+ const cy = project_y(pos_c[0], pos_c[1], pos_c[2]);
467
+
468
+ // in-plane gradient of the (linear) field; descend it (negative gradient points at the goal)
469
+ const grad_x = (gb - ga) / bx;
470
+ const grad_y = (gc - ga - grad_x * cx) / cy;
471
+
472
+ let dir_x = -grad_x;
473
+ let dir_y = -grad_y;
474
+ const dir_len = Math.hypot(dir_x, dir_y);
475
+
476
+ let next = NULL_POINTER;
477
+
478
+ if (dir_len > 1e-12) {
479
+ dir_x /= dir_len;
480
+ dir_y /= dir_len;
481
+
482
+ const loop_a = topology.face_read_loop(current);
483
+ const loop_b = topology.loop_read_next(loop_a);
484
+ const loop_c = topology.loop_read_next(loop_b);
485
+
486
+ next = pick_exit_edge(px, py, dir_x, dir_y, bx, cx, cy, topology, current, loop_a, loop_b, loop_c);
487
+
488
+ if (next !== NULL_POINTER && next !== previous && !visited_faces.has(next)) {
489
+ // follow the gradient: re-enter the next face at the exit point (on the shared edge)
490
+ const exit_x = px + dir_x * last_exit_t;
491
+ const exit_y = py + dir_y * last_exit_t;
492
+
493
+ world_x = pos_a[0] + exit_x * basis_u[0] + exit_y * basis_v[0];
494
+ world_y = pos_a[1] + exit_x * basis_u[1] + exit_y * basis_v[1];
495
+ world_z = pos_a[2] + exit_x * basis_u[2] + exit_y * basis_v[2];
496
+
497
+ previous = current;
498
+ current = next;
499
+ continue;
500
+ }
501
+ }
502
+
503
+ // the gradient gave no usable forward edge (a boundary/hole, a vertex hit, or it pointed into
504
+ // an already-traced face). Fall back to the lowest-field unvisited neighbour, re-entering at
505
+ // its centroid. If that happens we are near a hole, so the caller will likely re-run as a
506
+ // graph search; here we just keep the corridor connected.
507
+ next = lowest_unvisited_neighbour(topology, current);
508
+
509
+ if (next === NULL_POINTER) {
510
+ return 0;
511
+ }
512
+
513
+ face_centroid(topology, next);
514
+ world_x = scratch_centroid[0];
515
+ world_y = scratch_centroid[1];
516
+ world_z = scratch_centroid[2];
517
+
518
+ previous = current;
519
+ current = next;
520
+ }
521
+
522
+ return 0;
523
+ }
524
+
525
+ /**
526
+ * Walk parent pointers from goal back to start, writing the face sequence into `output` in
527
+ * START -> GOAL order, or 0 if the corridor exceeds `max_path_length`.
528
+ */
529
+ function construct_path(output, start_face, goal_face, max_path_length) {
530
+ let length = 0;
531
+ let current = goal_face;
532
+
533
+ while (true) {
534
+ if (length >= max_path_length) {
535
+ return 0;
536
+ }
537
+
538
+ output[length] = current;
539
+ length++;
540
+
541
+ if (current === start_face) {
542
+ break;
543
+ }
544
+
545
+ current = came_from.get(current);
546
+ }
547
+
548
+ const half_length = length >> 1;
549
+
550
+ for (let i = 0; i < half_length; i++) {
551
+ const mirror_i = length - 1 - i;
552
+ const temp = output[i];
553
+
554
+ output[i] = output[mirror_i];
555
+ output[mirror_i] = temp;
556
+ }
557
+
558
+ return length;
559
+ }
560
+
561
+ /**
562
+ * Fallback corridor finder: a best-first walk over faces, always expanding the one currently closest
563
+ * to the goal on the geodesic field. Always finds a corridor when one exists within the resolved field
564
+ * (it routes around holes the gradient trace could not), at the cost of not necessarily hugging the
565
+ * exact geodesic.
566
+ *
567
+ * @returns {number} faces written, or 0 if no path / it exceeds `max_path_length`
568
+ */
569
+ function search_corridor(output, topology, start_face, goal_face, max_path_length) {
570
+ open.clear();
571
+ came_from.clear();
572
+ visited_faces.clear();
573
+
574
+ open.insert(start_face, face_geodesic_distance(topology, start_face));
575
+ visited_faces.add(start_face);
576
+
577
+ while (!open.is_empty()) {
578
+ const current = open.pop_min();
579
+
580
+ if (current === goal_face) {
581
+ return construct_path(output, start_face, goal_face, max_path_length);
582
+ }
583
+
584
+ const count = bt_face_get_edge_adjacent_faces(neighbors, 0, topology, current);
585
+
586
+ for (let i = 0; i < count; i++) {
587
+ const neighbor = neighbors[i];
588
+
589
+ if (visited_faces.has(neighbor)) {
590
+ continue;
591
+ }
592
+
593
+ const distance = face_geodesic_distance(topology, neighbor);
594
+
595
+ if (distance === Infinity) {
596
+ continue; // outside the resolved field
597
+ }
598
+
599
+ visited_faces.add(neighbor);
600
+ came_from.set(neighbor, current);
601
+ open.insert(neighbor, distance);
602
+ }
603
+ }
604
+
605
+ return 0;
606
+ }
607
+
608
+ /**
609
+ * Extract the corridor from the resolved field: gradient trace first (hugs the geodesic), graph-walk
610
+ * fallback for winding around holes.
611
+ *
612
+ * @returns {number} faces written, or 0 if neither method produced a corridor
613
+ */
614
+ function extract_corridor(output, topology, start_face, goal_face, max_path_length) {
615
+ const traced = trace_corridor(output, topology, start_face, goal_face, max_path_length);
616
+
617
+ if (traced !== 0) {
618
+ return traced;
619
+ }
620
+
621
+ return search_corridor(output, topology, start_face, goal_face, max_path_length);
622
+ }
623
+
624
+ /**
625
+ * Find a shortest path through topology faces.
626
+ * If a path is found - the result will contain start and goal faces.
627
+ *
628
+ * The corridor is steered by an exact-geodesic distance field rather than a face-graph metric. A
629
+ * goal-directed Fast Marching pass (exact Eikonal triangle update) solves the true along-surface
630
+ * distance-to-goal at each vertex, sweeping toward the start and stopping as soon as the start face is
631
+ * resolved. Because the field measures the real walked distance (not centroid-to-centroid hops), the
632
+ * corridor heads straight at the goal and crosses a finely tessellated patch instead of skirting around
633
+ * it. The corridor is then extracted by descending the field's gradient from the start (which hugs the
634
+ * geodesic, so the downstream funnel/string-pull recovers the exact shortest path); where the gradient
635
+ * is an unreliable guide -- winding around a hole -- a best-first graph walk on the same field takes
636
+ * over. A full (non-early-terminated) solve is used as a backstop for the rare case the bounded fill
637
+ * left the corridor under-resolved.
638
+ *
639
+ * NOTE: if either start or goal faces are not part of the topology - an empty path will be produced.
640
+ *
641
+ * @param {number[]|Uint32Array} output path will be written here as a sequence of face IDs
642
+ * @param {number} start_face_id
643
+ * @param {number} goal_face_id
644
+ * @param {BinaryTopology} topology
645
+ * @param {number} [max_path_length=Infinity] cap on the number of faces written to `output`. If the
646
+ * shortest corridor is longer than this it does not fit the buffer and 0 is returned. Pass the
647
+ * capacity of `output` to guarantee no out-of-bounds writes.
648
+ * @returns {number} number of faces that make up the path. This will be 0 if no path is found (or the
649
+ * path does not fit `max_path_length`).
650
+ */
651
+ export function bt_mesh_face_find_path(
652
+ output,
653
+ start_face_id,
654
+ goal_face_id,
655
+ topology,
656
+ max_path_length = Infinity,
657
+ ) {
658
+ assert.isArrayLike(output, 'output');
659
+ assert.isNonNegativeInteger(start_face_id, 'start_face_id');
660
+ assert.isNonNegativeInteger(goal_face_id, 'goal_face_id');
661
+ assert.notNull(topology, 'topology');
662
+ assert.defined(topology, 'topology');
663
+ assert.equal(topology.isBinaryTopology, true, 'topology.isBinaryTopology !== true');
664
+
665
+ if (start_face_id === goal_face_id) {
666
+ // trivial path; still validate the face actually exists
667
+ if (!topology.faces.is_allocated(start_face_id)) {
668
+ return 0;
669
+ }
670
+
671
+ if (max_path_length < 1) {
672
+ return 0;
673
+ }
674
+
675
+ output[0] = start_face_id;
676
+ return 1;
677
+ }
678
+
679
+ // both faces must be part of the topology
680
+ if (!topology.faces.is_allocated(start_face_id) || !topology.faces.is_allocated(goal_face_id)) {
681
+ return 0;
682
+ }
683
+
684
+ // Fast path: goal-directed fill bounded to the start<->goal region. When the start is unreachable
685
+ // this drains exhaustively (it never early-exits), so a false return is a definitive "no path" --
686
+ // no second solve needed.
687
+ if (!solve_geodesic_field(topology, goal_face_id, start_face_id, true)) {
688
+ return 0; // no path between the two faces
689
+ }
690
+
691
+ const corridor = extract_corridor(output, topology, start_face_id, goal_face_id, max_path_length);
692
+
693
+ if (corridor !== 0) {
694
+ return corridor;
695
+ }
696
+
697
+ // Backstop: the bounded fill resolved enough to reach the start but left the corridor
698
+ // under-resolved for the extractor (rare; a corridor that winds far around a hole). A full solve
699
+ // resolves the whole reachable component, then the extractor cannot fail for a connected pair.
700
+ solve_geodesic_field(topology, goal_face_id, start_face_id, false);
701
+
702
+ return extract_corridor(output, topology, start_face_id, goal_face_id, max_path_length);
703
+ }