@woosh/meep-engine 2.168.0 → 2.169.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (295) hide show
  1. package/README.md +2 -2
  2. package/build/bundle-worker-image-decoder.js +1 -1
  3. package/package.json +1 -1
  4. package/src/REVIEW_2026_08_06/adv-core.md +511 -0
  5. package/src/REVIEW_2026_08_06/adv-gfx.md +480 -0
  6. package/src/REVIEW_2026_08_06/adv-view.md +446 -0
  7. package/src/REVIEW_2026_08_06/api-core.md +296 -0
  8. package/src/REVIEW_2026_08_06/api-engine.md +195 -0
  9. package/src/REVIEW_2026_08_06/bvh-phys-sound-net.md +507 -0
  10. package/src/REVIEW_2026_08_06/core-binary.md +348 -0
  11. package/src/REVIEW_2026_08_06/core-collection.md +376 -0
  12. package/src/REVIEW_2026_08_06/core-math.md +221 -0
  13. package/src/REVIEW_2026_08_06/core-process.md +266 -0
  14. package/src/REVIEW_2026_08_06/core-science.md +223 -0
  15. package/src/REVIEW_2026_08_06/engine-ai-nav.md +279 -0
  16. package/src/REVIEW_2026_08_06/engine-animation.md +167 -0
  17. package/src/REVIEW_2026_08_06/engine-asset.md +166 -0
  18. package/src/REVIEW_2026_08_06/engine-ecs.md +920 -0
  19. package/src/REVIEW_2026_08_06/engine-input.md +197 -0
  20. package/src/REVIEW_2026_08_06/engine-misc.md +248 -0
  21. package/src/REVIEW_2026_08_06/engine-terrain.md +52 -0
  22. package/src/REVIEW_2026_08_06/generation.md +176 -0
  23. package/src/REVIEW_2026_08_06/geom-2d.md +434 -0
  24. package/src/REVIEW_2026_08_06/geom-3d.md +117 -0
  25. package/src/REVIEW_2026_08_06/gfx-gi.md +143 -0
  26. package/src/REVIEW_2026_08_06/gfx-render.md +740 -0
  27. package/src/REVIEW_2026_08_06/gfx-texture.md +690 -0
  28. package/src/REVIEW_2026_08_06/prior-criticals.md +49 -0
  29. package/src/REVIEW_2026_08_06/prior-highs.md +138 -0
  30. package/src/REVIEW_2026_08_06/test-gaps.md +364 -0
  31. package/src/REVIEW_2026_08_06/view.md +667 -0
  32. package/src/REVIEW_2026_08_06.md +571 -0
  33. package/src/core/binary/BinaryBuffer.d.ts.map +1 -1
  34. package/src/core/binary/BinaryBuffer.js +12 -1
  35. package/src/core/binary/BitImage2.d.ts +5 -1
  36. package/src/core/binary/BitImage2.d.ts.map +1 -1
  37. package/src/core/binary/BitImage2.js +5 -1
  38. package/src/core/bvh8/build/NodeProxy.d.ts.map +1 -1
  39. package/src/core/bvh8/build/NodeProxy.js +314 -308
  40. package/src/core/collection/list/List.d.ts +15 -0
  41. package/src/core/collection/list/List.d.ts.map +1 -1
  42. package/src/core/collection/list/List.js +34 -0
  43. package/src/core/collection/list/SortedListProjection.d.ts.map +1 -1
  44. package/src/core/collection/list/SortedListProjection.js +4 -1
  45. package/src/core/collection/map/BiMap.d.ts +25 -2
  46. package/src/core/collection/map/BiMap.d.ts.map +1 -1
  47. package/src/core/collection/map/BiMap.js +57 -3
  48. package/src/core/debug/matchers/IsAnything.d.ts.map +1 -1
  49. package/src/core/debug/matchers/IsAnything.js +5 -1
  50. package/src/core/events/signal/Signal.d.ts.map +1 -1
  51. package/src/core/events/signal/Signal.js +3 -1
  52. package/src/core/geom/2d/polygon/TRIANGULATION_DESIGN.md +433 -0
  53. package/src/core/geom/2d/polygon/polygon2_is_counter_clockwise.d.ts +31 -0
  54. package/src/core/geom/2d/polygon/polygon2_is_counter_clockwise.d.ts.map +1 -0
  55. package/src/core/geom/2d/polygon/polygon2_is_counter_clockwise.js +84 -0
  56. package/src/core/geom/2d/polygon/polygon2_signed_area.d.ts +24 -2
  57. package/src/core/geom/2d/polygon/polygon2_signed_area.d.ts.map +1 -1
  58. package/src/core/geom/2d/polygon/polygon2_signed_area.js +52 -34
  59. package/src/core/geom/2d/polygon/polygon2_triangulate.corpus.d.ts +28 -0
  60. package/src/core/geom/2d/polygon/polygon2_triangulate.corpus.d.ts.map +1 -0
  61. package/src/core/geom/2d/polygon/polygon2_triangulate.corpus.js +86 -0
  62. package/src/core/geom/2d/polygon/polygon2_triangulate.d.ts +40 -0
  63. package/src/core/geom/2d/polygon/polygon2_triangulate.d.ts.map +1 -0
  64. package/src/core/geom/2d/polygon/polygon2_triangulate.js +1280 -0
  65. package/src/core/geom/2d/polygon/polygon2_triangulation_deviation.d.ts +28 -0
  66. package/src/core/geom/2d/polygon/polygon2_triangulation_deviation.d.ts.map +1 -0
  67. package/src/core/geom/2d/polygon/polygon2_triangulation_deviation.js +77 -0
  68. package/src/core/geom/2d/triangle/tri2_rasterize_conservative.d.ts +6 -3
  69. package/src/core/geom/2d/triangle/tri2_rasterize_conservative.d.ts.map +1 -1
  70. package/src/core/geom/2d/triangle/tri2_rasterize_conservative.js +26 -13
  71. package/src/core/geom/2d/triangle/tri2_signed_area.d.ts +17 -1
  72. package/src/core/geom/2d/triangle/tri2_signed_area.d.ts.map +1 -1
  73. package/src/core/geom/2d/triangle/tri2_signed_area.js +35 -23
  74. package/src/core/geom/2d/v2_morton_encode.d.ts +20 -0
  75. package/src/core/geom/2d/v2_morton_encode.d.ts.map +1 -0
  76. package/src/core/geom/2d/v2_morton_encode.js +23 -0
  77. package/src/core/geom/3d/atlas/segment/atlas_merge_charts.d.ts.map +1 -1
  78. package/src/core/geom/3d/atlas/segment/atlas_merge_charts.js +4 -1
  79. package/src/core/geom/3d/polygon/polygon3_compute_normal.d.ts +27 -0
  80. package/src/core/geom/3d/polygon/polygon3_compute_normal.d.ts.map +1 -0
  81. package/src/core/geom/3d/polygon/polygon3_compute_normal.js +60 -0
  82. package/src/core/geom/3d/polygon/polygon3_triangulate.d.ts +34 -0
  83. package/src/core/geom/3d/polygon/polygon3_triangulate.d.ts.map +1 -0
  84. package/src/core/geom/3d/polygon/polygon3_triangulate.js +94 -0
  85. package/src/core/geom/3d/quaternion/quat3_multiply.d.ts +8 -2
  86. package/src/core/geom/3d/quaternion/quat3_multiply.d.ts.map +1 -1
  87. package/src/core/geom/3d/quaternion/quat3_multiply.js +8 -2
  88. package/src/core/geom/3d/shape/ConvexHullShape3D.d.ts.map +1 -1
  89. package/src/core/geom/3d/shape/ConvexHullShape3D.js +32 -2
  90. package/src/core/geom/3d/topology/struct/binary/io/face/bt_face_triangulate.d.ts +23 -6
  91. package/src/core/geom/3d/topology/struct/binary/io/face/bt_face_triangulate.d.ts.map +1 -1
  92. package/src/core/geom/3d/topology/struct/binary/io/face/bt_face_triangulate.js +268 -124
  93. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_assert_valid.d.ts +17 -0
  94. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_assert_valid.d.ts.map +1 -0
  95. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_assert_valid.js +44 -0
  96. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_count_edge_faces.d.ts +20 -0
  97. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_count_edge_faces.d.ts.map +1 -0
  98. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_count_edge_faces.js +40 -0
  99. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_is_closed.d.ts +19 -0
  100. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_is_closed.d.ts.map +1 -0
  101. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_is_closed.js +34 -0
  102. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_is_manifold.d.ts +22 -0
  103. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_is_manifold.d.ts.map +1 -0
  104. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_is_manifold.js +37 -0
  105. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_validate.d.ts +87 -0
  106. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_validate.d.ts.map +1 -0
  107. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_validate.js +599 -0
  108. package/src/core/geom/3d/triangle/tri3_compute_signed_volume.d.ts +32 -0
  109. package/src/core/geom/3d/triangle/tri3_compute_signed_volume.d.ts.map +1 -1
  110. package/src/core/geom/3d/triangle/tri3_compute_signed_volume.js +66 -7
  111. package/src/core/geom/3d/triangle/tri3_mesh_compute_signed_volume.d.ts +22 -1
  112. package/src/core/geom/3d/triangle/tri3_mesh_compute_signed_volume.d.ts.map +1 -1
  113. package/src/core/geom/3d/triangle/tri3_mesh_compute_signed_volume.js +83 -38
  114. package/src/core/geom/MANIFOLD_COMPARISON_2026_08_01.md +554 -0
  115. package/src/core/geom/vec3/v3_cotangent.d.ts +6 -2
  116. package/src/core/geom/vec3/v3_cotangent.d.ts.map +1 -1
  117. package/src/core/geom/vec3/v3_cotangent.js +6 -2
  118. package/src/core/geom/vec3/v3_cross.d.ts +6 -2
  119. package/src/core/geom/vec3/v3_cross.d.ts.map +1 -1
  120. package/src/core/geom/vec3/v3_cross.js +6 -2
  121. package/src/core/geom/vec3/v3_multiply.d.ts +6 -2
  122. package/src/core/geom/vec3/v3_multiply.d.ts.map +1 -1
  123. package/src/core/geom/vec3/v3_multiply.js +6 -2
  124. package/src/core/geom/vec3/v3_subtract.d.ts +6 -2
  125. package/src/core/geom/vec3/v3_subtract.d.ts.map +1 -1
  126. package/src/core/geom/vec3/v3_subtract.js +6 -2
  127. package/src/core/localization/Localization.js +1 -1
  128. package/src/core/math/random/randomSeed.d.ts +13 -0
  129. package/src/core/math/random/randomSeed.d.ts.map +1 -0
  130. package/src/core/math/random/randomSeed.js +16 -0
  131. package/src/core/math/spline/spline3_hermite_apply_transform.d.ts +50 -0
  132. package/src/core/math/spline/spline3_hermite_apply_transform.d.ts.map +1 -0
  133. package/src/core/math/spline/spline3_hermite_apply_transform.js +107 -0
  134. package/src/core/math/spline/spline3_hermite_reverse.d.ts +34 -0
  135. package/src/core/math/spline/spline3_hermite_reverse.d.ts.map +1 -0
  136. package/src/core/math/spline/spline3_hermite_reverse.js +49 -0
  137. package/src/core/math/statistics/hammersley_sequence.d.ts +17 -4
  138. package/src/core/math/statistics/hammersley_sequence.d.ts.map +1 -1
  139. package/src/core/math/statistics/hammersley_sequence.js +25 -16
  140. package/src/core/process/Future.d.ts.map +1 -1
  141. package/src/core/process/Future.js +12 -0
  142. package/src/core/process/PromiseWatcher.d.ts.map +1 -1
  143. package/src/core/process/PromiseWatcher.js +9 -2
  144. package/src/core/process/executor/ConcurrentExecutor.d.ts.map +1 -1
  145. package/src/core/process/executor/ConcurrentExecutor.js +12 -2
  146. package/src/core/process/worker/WorkerProxy.d.ts.map +1 -1
  147. package/src/core/process/worker/WorkerProxy.js +5 -2
  148. package/src/core/process/worker/extractTransferables.d.ts.map +1 -1
  149. package/src/core/process/worker/extractTransferables.js +4 -1
  150. package/src/engine/Engine.d.ts +12 -12
  151. package/src/engine/Engine.d.ts.map +1 -1
  152. package/src/engine/Engine.js +149 -10
  153. package/src/engine/EngineHarness.d.ts.map +1 -1
  154. package/src/engine/EngineHarness.js +4 -1
  155. package/src/engine/asset/loaders/ArrayBufferLoader.d.ts.map +1 -1
  156. package/src/engine/asset/loaders/ArrayBufferLoader.js +7 -0
  157. package/src/engine/ecs/speaker/lines/sets/LineSetDescription.d.ts.map +1 -1
  158. package/src/engine/ecs/speaker/lines/sets/LineSetDescription.js +3 -1
  159. package/src/engine/ecs/terrain/ecs/TerrainSystem.d.ts.map +1 -1
  160. package/src/engine/ecs/terrain/ecs/TerrainSystem.js +4 -1
  161. package/src/engine/ecs/terrain/tiles/TerrainTileManager.d.ts +29 -0
  162. package/src/engine/ecs/terrain/tiles/TerrainTileManager.d.ts.map +1 -1
  163. package/src/engine/ecs/terrain/tiles/TerrainTileManager.js +84 -23
  164. package/src/engine/graphics/CONTEXT_LOSS_RECOVERY_PLAN.md +446 -0
  165. package/src/engine/graphics/GraphicsEngine.d.ts +56 -7
  166. package/src/engine/graphics/GraphicsEngine.d.ts.map +1 -1
  167. package/src/engine/graphics/GraphicsEngine.js +196 -24
  168. package/src/engine/graphics/context/WebGLContextFailureReason.d.ts +9 -0
  169. package/src/engine/graphics/context/WebGLContextFailureReason.d.ts.map +1 -0
  170. package/src/engine/graphics/context/WebGLContextFailureReason.js +17 -0
  171. package/src/engine/graphics/context/WebGLContextMonitor.d.ts +106 -0
  172. package/src/engine/graphics/context/WebGLContextMonitor.d.ts.map +1 -0
  173. package/src/engine/graphics/context/WebGLContextMonitor.js +341 -0
  174. package/src/engine/graphics/context/WebGLContextState.d.ts +14 -0
  175. package/src/engine/graphics/context/WebGLContextState.d.ts.map +1 -0
  176. package/src/engine/graphics/context/WebGLContextState.js +24 -0
  177. package/src/engine/graphics/context/testWebGLContextLoss.d.ts +2 -0
  178. package/src/engine/graphics/context/testWebGLContextLoss.d.ts.map +1 -0
  179. package/src/engine/graphics/context/testWebGLContextLoss.js +696 -0
  180. package/src/engine/graphics/ecs/camera/serialization/CameraSerializationAdapter.d.ts.map +1 -1
  181. package/src/engine/graphics/ecs/camera/serialization/CameraSerializationAdapter.js +68 -32
  182. package/src/engine/graphics/ecs/camera/serialization/CameraSerializationUpgrader_0_1.d.ts +14 -0
  183. package/src/engine/graphics/ecs/camera/serialization/CameraSerializationUpgrader_0_1.d.ts.map +1 -0
  184. package/src/engine/graphics/ecs/camera/serialization/CameraSerializationUpgrader_0_1.js +41 -0
  185. package/src/engine/graphics/ecs/decal/v2/FPDecalSystem.js +1 -1
  186. package/src/engine/graphics/ecs/mesh-v2/ShadedGeometrySystem.d.ts.map +1 -1
  187. package/src/engine/graphics/ecs/mesh-v2/ShadedGeometrySystem.js +5 -0
  188. package/src/engine/graphics/ecs/trail2d/Trail2D.d.ts +5 -1
  189. package/src/engine/graphics/ecs/trail2d/Trail2D.d.ts.map +1 -1
  190. package/src/engine/graphics/ecs/trail2d/Trail2D.js +15 -0
  191. package/src/engine/graphics/ecs/trail2d/Trail2DSystem.d.ts +1 -1
  192. package/src/engine/graphics/ecs/trail2d/Trail2DSystem.d.ts.map +1 -1
  193. package/src/engine/graphics/ecs/trail2d/Trail2DSystem.js +2 -1
  194. package/src/engine/graphics/ecs/trail3d/Trail3D.d.ts +4 -1
  195. package/src/engine/graphics/ecs/trail3d/Trail3D.d.ts.map +1 -1
  196. package/src/engine/graphics/ecs/trail3d/Trail3D.js +488 -476
  197. package/src/engine/graphics/ecs/trail3d/Trail3DSystem.d.ts +1 -1
  198. package/src/engine/graphics/ecs/trail3d/Trail3DSystem.d.ts.map +1 -1
  199. package/src/engine/graphics/ecs/trail3d/Trail3DSystem.js +280 -253
  200. package/src/engine/graphics/geometry/MikkT/BuildNeighborsFast.d.ts.map +1 -1
  201. package/src/engine/graphics/geometry/MikkT/BuildNeighborsFast.js +3 -2
  202. package/src/engine/graphics/geometry/MikkT/GenerateSharedVerticesIndexList.d.ts.map +1 -1
  203. package/src/engine/graphics/geometry/MikkT/GenerateSharedVerticesIndexList.js +3 -1
  204. package/src/engine/graphics/geometry/computeMeshSurfaceArea.js +1 -1
  205. package/src/engine/graphics/load_and_set_cubemap_v0.d.ts +5 -0
  206. package/src/engine/graphics/load_and_set_cubemap_v0.d.ts.map +1 -1
  207. package/src/engine/graphics/load_and_set_cubemap_v0.js +26 -21
  208. package/src/engine/graphics/particles/particular/group/ParticleGroup.d.ts.map +1 -1
  209. package/src/engine/graphics/particles/particular/group/ParticleGroup.js +19 -8
  210. package/src/engine/graphics/particles/particular/group/optimizeCommandQueue.js +1 -1
  211. package/src/engine/graphics/texture/atlas/AtlasLookupTexture.d.ts.map +1 -1
  212. package/src/engine/graphics/texture/atlas/AtlasLookupTexture.js +10 -3
  213. package/src/engine/graphics/texture/atlas/TextureAtlas.d.ts.map +1 -1
  214. package/src/engine/graphics/texture/atlas/TextureAtlas.js +4 -1
  215. package/src/engine/graphics/texture/atlas/gpu/WebGLTextureAtlas.d.ts.map +1 -1
  216. package/src/engine/graphics/texture/atlas/gpu/WebGLTextureAtlas.js +11 -0
  217. package/src/engine/graphics/texture/cubemap/load_environment_map.d.ts.map +1 -1
  218. package/src/engine/graphics/texture/cubemap/load_environment_map.js +5 -2
  219. package/src/engine/graphics/texture/sampler/distance/computeSignedDistanceField_Chamfer.d.ts.map +1 -1
  220. package/src/engine/graphics/texture/sampler/distance/computeSignedDistanceField_Chamfer.js +26 -18
  221. package/src/engine/graphics/texture/sampler/filter/sampler2d_blur_gaussian.d.ts +15 -2
  222. package/src/engine/graphics/texture/sampler/filter/sampler2d_blur_gaussian.d.ts.map +1 -1
  223. package/src/engine/graphics/texture/sampler/filter/sampler2d_blur_gaussian.js +29 -3
  224. package/src/engine/graphics/texture/sampler/resize/sampler2d_scale_down_lanczos.d.ts +11 -1
  225. package/src/engine/graphics/texture/sampler/resize/sampler2d_scale_down_lanczos.d.ts.map +1 -1
  226. package/src/engine/graphics/texture/sampler/resize/sampler2d_scale_down_lanczos.js +158 -171
  227. package/src/engine/graphics/texture/sampler/sampler2d_paint.js +1 -1
  228. package/src/engine/graphics/texture/sampler/util/bitSet2Sampler2D.js +2 -2
  229. package/src/engine/input/ecs/util/TopDownCameraControllerHelper.d.ts.map +1 -1
  230. package/src/engine/input/ecs/util/TopDownCameraControllerHelper.js +10 -3
  231. package/src/engine/navigation/ecs/components/PathSerializationUpgrader_2_3.d.ts +14 -0
  232. package/src/engine/navigation/ecs/components/PathSerializationUpgrader_2_3.d.ts.map +1 -0
  233. package/src/engine/navigation/ecs/components/PathSerializationUpgrader_2_3.js +38 -0
  234. package/src/engine/network/orchestrator/ServerAuthoritativeClient.d.ts.map +1 -1
  235. package/src/engine/network/orchestrator/ServerAuthoritativeClient.js +433 -425
  236. package/src/engine/physics/constraint/solve_constraints.d.ts +4 -1
  237. package/src/engine/physics/constraint/solve_constraints.d.ts.map +1 -1
  238. package/src/engine/physics/constraint/solve_constraints.js +38 -13
  239. package/src/engine/physics/contact/ManifoldStore.d.ts +15 -3
  240. package/src/engine/physics/contact/ManifoldStore.d.ts.map +1 -1
  241. package/src/engine/physics/contact/ManifoldStore.js +15 -3
  242. package/src/engine/physics/ecs/Joint.d.ts +7 -4
  243. package/src/engine/physics/ecs/Joint.d.ts.map +1 -1
  244. package/src/engine/physics/ecs/Joint.js +7 -4
  245. package/src/engine/physics/ecs/PhysicsSystem.d.ts +14 -0
  246. package/src/engine/physics/ecs/PhysicsSystem.d.ts.map +1 -1
  247. package/src/engine/physics/ecs/PhysicsSystem.js +16 -0
  248. package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
  249. package/src/engine/physics/fluid/ecs/FluidSystem.d.ts +3 -3
  250. package/src/engine/physics/narrowphase/convex_convex_manifold.d.ts +15 -7
  251. package/src/engine/physics/narrowphase/convex_convex_manifold.d.ts.map +1 -1
  252. package/src/engine/physics/narrowphase/convex_convex_manifold.js +33 -10
  253. package/src/engine/physics/narrowphase/refine_ray_concave.d.ts +6 -2
  254. package/src/engine/physics/narrowphase/refine_ray_concave.d.ts.map +1 -1
  255. package/src/engine/physics/narrowphase/refine_ray_concave.js +6 -2
  256. package/src/engine/physics/narrowphase/refine_ray_hit.d.ts +6 -2
  257. package/src/engine/physics/narrowphase/refine_ray_hit.d.ts.map +1 -1
  258. package/src/engine/physics/narrowphase/refine_ray_hit.js +6 -2
  259. package/src/engine/physics/queries/raycast.d.ts.map +1 -1
  260. package/src/engine/physics/queries/raycast.js +11 -4
  261. package/src/engine/save/storage/IndexedDBStorage.d.ts.map +1 -1
  262. package/src/engine/save/storage/IndexedDBStorage.js +21 -3
  263. package/src/engine/simulation/Ticker.d.ts +12 -0
  264. package/src/engine/simulation/Ticker.d.ts.map +1 -1
  265. package/src/engine/simulation/Ticker.js +18 -0
  266. package/src/engine/sound/simulation/AcousticSimulator.d.ts.map +1 -1
  267. package/src/engine/sound/simulation/AcousticSimulator.js +9 -1
  268. package/src/engine/sound/simulation/core/VolumeField.d.ts +6 -2
  269. package/src/engine/sound/simulation/core/VolumeField.d.ts.map +1 -1
  270. package/src/engine/sound/simulation/core/VolumeField.js +6 -2
  271. package/src/engine/sound/simulation/probe/AcousticProbeField.d.ts +6 -2
  272. package/src/engine/sound/simulation/probe/AcousticProbeField.d.ts.map +1 -1
  273. package/src/engine/sound/simulation/probe/AcousticProbeField.js +6 -2
  274. package/src/engine/sound/simulation/probe/acoustic_probe_transfer.d.ts +6 -2
  275. package/src/engine/sound/simulation/probe/acoustic_probe_transfer.d.ts.map +1 -1
  276. package/src/engine/sound/simulation/probe/acoustic_probe_transfer.js +6 -2
  277. package/src/engine/sound/simulation/probe/bakeProbeReflectors.d.ts +3 -1
  278. package/src/engine/sound/simulation/probe/bakeProbeReflectors.d.ts.map +1 -1
  279. package/src/engine/sound/simulation/probe/bakeProbeReflectors.js +3 -1
  280. package/src/engine/ui/GUIEngine.d.ts.map +1 -1
  281. package/src/engine/ui/GUIEngine.js +8 -1
  282. package/src/generation/theme/TerrainTheme.d.ts.map +1 -1
  283. package/src/generation/theme/TerrainTheme.js +2 -1
  284. package/src/generation/theme/ThemeEngine.d.ts.map +1 -1
  285. package/src/generation/theme/ThemeEngine.js +6 -5
  286. package/src/view/ViewGroup.d.ts.map +1 -1
  287. package/src/view/ViewGroup.js +9 -7
  288. package/src/view/graphics/WebGLContextFailureView.d.ts +19 -0
  289. package/src/view/graphics/WebGLContextFailureView.d.ts.map +1 -0
  290. package/src/view/graphics/WebGLContextFailureView.js +76 -0
  291. package/src/view/minimap/Minimap.d.ts +8 -0
  292. package/src/view/minimap/Minimap.d.ts.map +1 -1
  293. package/src/view/minimap/Minimap.js +16 -3
  294. package/src/view/minimap/dom/MinimapMarkerView.d.ts.map +1 -1
  295. package/src/view/minimap/dom/MinimapMarkerView.js +3 -1
@@ -113,10 +113,34 @@ export class ConvexHullShape3D extends AbstractShape3D {
113
113
  const b = this.__bbox;
114
114
  b[0] = mnx; b[1] = mny; b[2] = mnz; b[3] = mxx; b[4] = mxy; b[5] = mxz;
115
115
 
116
+ // Volume is accumulated about the hull's own bbox centre, not the
117
+ // origin. Σ a·(b×c) is translation invariant over a closed surface --
118
+ // shifting `a` by a constant r changes the sum by r·Σn, and the face
119
+ // area vectors of a closed surface sum to zero -- so the apex may be
120
+ // moved freely, and moving it onto the hull is what keeps the terms on
121
+ // the scale of the hull instead of on the scale of its distance from
122
+ // the origin. Anchored at the origin, a hull sitting 1e6 away produced
123
+ // terms around 1e18 that had to cancel down to its volume, and the
124
+ // reported volume drifted by ~7e-4 relative for a hull whose vertices
125
+ // were stored exactly.
126
+ //
127
+ // Σn = 0 needs the surface to be closed and consistently wound, which
128
+ // `from` does not check. For a surface that is not -- an open cap, a
129
+ // flipped or duplicated face -- this reports a different number than
130
+ // anchoring at the origin did. Both are meaningless for such input, but
131
+ // note the old one was meaningless in a more flattering way: a face
132
+ // touching the origin contributed nothing, so a broken hull could still
133
+ // read a plausible 1.0 at the origin and something else entirely once
134
+ // translated. This number is at least a property of the surface rather
135
+ // than of where it sits.
136
+ const ox = (mnx + mxx) * 0.5;
137
+ const oy = (mny + mxy) * 0.5;
138
+ const oz = (mnz + mxz) * 0.5;
139
+
116
140
  const tri = idx.length / 3;
117
141
  const planes = new Float64Array(tri * 4);
118
142
  let area = 0;
119
- let vol6 = 0; // 6× the signed volume (Σ a·(b×c) over outward faces)
143
+ let vol6 = 0; // 6× the signed volume (Σ (a − o)·n over outward faces)
120
144
  for (let f = 0; f < tri; f++) {
121
145
  const ia = idx[f * 3] * 3, ib = idx[f * 3 + 1] * 3, ic = idx[f * 3 + 2] * 3;
122
146
  const ax = v[ia], ay = v[ia + 1], az = v[ia + 2];
@@ -130,7 +154,13 @@ export class ConvexHullShape3D extends AbstractShape3D {
130
154
  let nz = e1x * e2y - e1y * e2x;
131
155
  const len = Math.sqrt(nx * nx + ny * ny + nz * nz) || 1;
132
156
  area += 0.5 * len;
133
- vol6 += ax * (by * cz - bz * cy) + ay * (bz * cx - bx * cz) + az * (bx * cy - by * cx);
157
+ // Two rewrites, both exact, and together they remove every product
158
+ // of absolute coordinates:
159
+ // a·(b × c) === a·((b − a) × (c − a)), since a·(b × a) and
160
+ // a·(a × c) are both zero -- so the normal already computed above
161
+ // can be reused in place of the b × c cross product;
162
+ // Σ a·n === Σ (a − o)·n over a closed surface, since Σ n = 0.
163
+ vol6 += (ax - ox) * nx + (ay - oy) * ny + (az - oz) * nz;
134
164
 
135
165
  nx /= len; ny /= len; nz /= len;
136
166
  const o = f * 4;
@@ -1,13 +1,30 @@
1
1
  /**
2
- * Triangulate a face in a binary topology mesh.
2
+ * Split one face of a {@link BinaryTopology} into triangles, in place.
3
3
  *
4
- * Assumes that each face is a convex polygon.
4
+ * Handles any simple polygon, convex or not: the corner order is projected onto
5
+ * the plane of the face's own Newell normal and triangulated by
6
+ * {@link polygon3_triangulate}, whose decisions are exact. The resulting ear
7
+ * sequence is then replayed as topology surgery, so existing corners keep their
8
+ * identity (and any per-loop data) and only the diagonals are new. Each new face
9
+ * inherits the original's stored normal.
5
10
  *
6
- * If the face is already a triangle, no action is taken.
11
+ * Superseded the previous fan-from-the-first-corner implementation, which was
12
+ * correct only for convex faces and produced silently overlapping triangles for
13
+ * anything else.
14
+ *
15
+ * Nothing is changed, and `1` is returned, when the face is already a triangle,
16
+ * has no area (so no plane to project onto), or projects to a self-intersecting
17
+ * outline -- which is a non-planar or otherwise malformed face, and cannot be cut
18
+ * into ears at all. Leaving it whole is the honest outcome; half-cutting it would
19
+ * corrupt the mesh.
7
20
  *
8
21
  * @param {BinaryTopology} mesh
9
- * @param {number} face Face ID
10
- * @return {boolean} True if the face was triangulated, false if it was already a triangle.
22
+ * @param {number} face
23
+ * @returns {number} how many faces the input face became: `1` when it was left
24
+ * untouched, otherwise the number of triangles it was split into
25
+ *
26
+ * @author Alex Goldring
27
+ * @copyright Company Named Limited (c) 2026
11
28
  */
12
- export function bt_face_triangulate(mesh: BinaryTopology, face: number): boolean;
29
+ export function bt_face_triangulate(mesh: BinaryTopology, face: number): number;
13
30
  //# sourceMappingURL=bt_face_triangulate.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"bt_face_triangulate.d.ts","sourceRoot":"","sources":["../../../../../../../../../../src/core/geom/3d/topology/struct/binary/io/face/bt_face_triangulate.js"],"names":[],"mappings":"AAIA;;;;;;;;;;GAUG;AACH,gEAHW,MAAM,GACL,OAAO,CA8GlB"}
1
+ {"version":3,"file":"bt_face_triangulate.d.ts","sourceRoot":"","sources":["../../../../../../../../../../src/core/geom/3d/topology/struct/binary/io/face/bt_face_triangulate.js"],"names":[],"mappings":"AAqKA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,gEAPW,MAAM,GACJ,MAAM,CAgFlB"}
@@ -1,124 +1,268 @@
1
- import { NULL_POINTER } from "../../BinaryTopology.js";
2
- import { bt_radial_loop_add } from "../bt_radial_loop_add.js";
3
- import { bt_edge_create } from "../edge/bt_edge_create.js";
4
-
5
- /**
6
- * Triangulate a face in a binary topology mesh.
7
- *
8
- * Assumes that each face is a convex polygon.
9
- *
10
- * If the face is already a triangle, no action is taken.
11
- *
12
- * @param {BinaryTopology} mesh
13
- * @param {number} face Face ID
14
- * @return {boolean} True if the face was triangulated, false if it was already a triangle.
15
- */
16
- export function bt_face_triangulate(
17
- mesh,
18
- face
19
- ) {
20
- // 1. Validate Face and Count Loops
21
- const first_loop = mesh.face_read_loop(face);
22
- if (first_loop === NULL_POINTER){
23
- // should never happen, but just in case
24
- return false;
25
- }
26
-
27
- let count = 0;
28
- let iter = first_loop;
29
- do {
30
- count++;
31
- iter = mesh.loop_read_next(iter);
32
- } while (iter !== first_loop);
33
-
34
- if (count < 4) {
35
- // looks like a triangle
36
- return false;
37
- }
38
-
39
- // 2. Prepare for Triangulation (Fan method at first vertex)
40
- let l_prev = first_loop; // Vertex 0 (Anchor)
41
- let l_curr = mesh.loop_read_next(l_prev); // Vertex 1
42
- let l_next = mesh.loop_read_next(l_curr); // Vertex 2
43
-
44
- const cuts = count - 3;
45
-
46
- // Cache normal to apply to new faces
47
- const normal = new Float32Array(3);
48
- mesh.face_read_normal(normal, 0, face);
49
-
50
- for (let i = 0; i < cuts; i++) {
51
- // We cut the triangle (l_prev, l_curr, l_next) out of 'face'.
52
- // New Edge connects V(l_prev) <-> V(l_next).
53
-
54
- const v_anchor = mesh.loop_read_vertex(l_prev);
55
- const v_target = mesh.loop_read_vertex(l_next);
56
-
57
- // --- A. Create Topology Elements ---
58
- // Reuse: Handles allocation and Vertex Disk Cycle linking
59
- const e_new = bt_edge_create(mesh, v_anchor, v_target);
60
-
61
- const f_new = mesh.faces.allocate();
62
- const l_tri = mesh.loop_create(); // Loop at v_target (for the Triangle)
63
- const l_poly = mesh.loop_create(); // Loop at v_anchor (for the Polygon)
64
-
65
- // --- B. Setup Loops & Radial Cycles ---
66
-
67
- // 1. Setup l_tri properties
68
- mesh.loop_write_vertex(l_tri, v_target);
69
- mesh.loop_write_face(l_tri, f_new);
70
-
71
- // 2. Setup l_poly properties
72
- mesh.loop_write_vertex(l_poly, v_anchor);
73
- mesh.loop_write_face(l_poly, face); // Stays part of the shrinking polygon
74
-
75
- // 3. Reuse: Link loops to the edge's Radial Cycle
76
- bt_radial_loop_add(mesh, l_tri, e_new);
77
- bt_radial_loop_add(mesh, l_poly, e_new);
78
-
79
- // --- C. Re-link Face Loop Topology (Next/Prev) ---
80
-
81
- // 1. Get the loop entering the anchor from the polygon side
82
- const l_prev_prev = mesh.loop_read_prev(l_prev);
83
-
84
- // 2. Assign l_prev and l_curr to the new Triangle Face
85
- mesh.loop_write_face(l_prev, f_new);
86
- mesh.loop_write_face(l_curr, f_new);
87
-
88
- // 3. Close the New Triangle (l_prev -> l_curr -> l_tri)
89
- mesh.loop_write_next(l_prev, l_curr);
90
- mesh.loop_write_prev(l_prev, l_tri);
91
-
92
- mesh.loop_write_next(l_curr, l_tri);
93
- mesh.loop_write_prev(l_curr, l_prev);
94
-
95
- mesh.loop_write_next(l_tri, l_prev);
96
- mesh.loop_write_prev(l_tri, l_curr);
97
-
98
- // Set properties for the new face
99
- mesh.face_write_loop(f_new, l_prev);
100
- mesh.face_write_normal(f_new, normal, 0);
101
-
102
- // 4. Close the Remaining Polygon (l_prev_prev -> l_poly -> l_next)
103
- mesh.loop_write_next(l_prev_prev, l_poly);
104
- // l_prev_prev's prev pointer remains unchanged
105
-
106
- mesh.loop_write_prev(l_poly, l_prev_prev);
107
- mesh.loop_write_next(l_poly, l_next);
108
-
109
- mesh.loop_write_prev(l_next, l_poly);
110
- // l_next's next pointer remains unchanged
111
-
112
- // Update Polygon Face pointer (ensure it points to a valid loop in the poly)
113
- mesh.face_write_loop(face, l_poly);
114
-
115
- // --- D. Advance Fan ---
116
- // The polygon now starts at l_poly (at v0).
117
- // The next triangle will use l_poly as its "prev", l_next as "curr".
118
- l_prev = l_poly;
119
- l_curr = l_next;
120
- l_next = mesh.loop_read_next(l_next);
121
- }
122
-
123
- return true;
124
- }
1
+ import { polygon3_compute_normal } from "../../../../../polygon/polygon3_compute_normal.js";
2
+ import { polygon3_triangulate } from "../../../../../polygon/polygon3_triangulate.js";
3
+ import { NULL_POINTER } from "../../BinaryTopology.js";
4
+ import { bt_radial_loop_add } from "../bt_radial_loop_add.js";
5
+ import { bt_edge_create } from "../edge/bt_edge_create.js";
6
+
7
+ // Per-call scratch, grown on demand. Faces are small, so these stay tiny in
8
+ // practice; module-static so triangulating a whole mesh allocates nothing.
9
+ let _loops = new Uint32Array(0);
10
+ let _positions = new Float64Array(0);
11
+ let _triangles = new Uint32Array(0);
12
+ let _ring_next = new Uint32Array(0);
13
+ let _ring_prev = new Uint32Array(0);
14
+
15
+ const _contour_ends = new Uint32Array(1);
16
+ const _normal = new Float64Array(3);
17
+ const _stored_normal = new Float32Array(3);
18
+ const _coordinate = new Float32Array(3);
19
+
20
+ /**
21
+ * @param {number} corner_count
22
+ */
23
+ function ensure_capacity(corner_count) {
24
+ if (_loops.length >= corner_count) {
25
+ return;
26
+ }
27
+
28
+ _loops = new Uint32Array(corner_count);
29
+ _positions = new Float64Array(corner_count * 3);
30
+ _triangles = new Uint32Array(Math.max(0, corner_count - 2) * 3);
31
+ _ring_next = new Uint32Array(corner_count);
32
+ _ring_prev = new Uint32Array(corner_count);
33
+ }
34
+
35
+ /**
36
+ * Cut the triangle `(prev(l_ear), l_ear, next(l_ear))` off `face` as a new face,
37
+ * leaving `face` one corner smaller.
38
+ *
39
+ * The two existing corners move to the new face and two fresh loops are created:
40
+ * one closing the triangle at the far vertex, one continuing the shrunken polygon
41
+ * at the near vertex. Both are attached to the new diagonal edge's radial cycle.
42
+ *
43
+ * @param {BinaryTopology} mesh
44
+ * @param {number} face
45
+ * @param {number} l_ear the corner being cut away
46
+ * @param {Float32Array} normal applied to the new face
47
+ * @returns {number} the replacement corner the shrunken polygon now has at the
48
+ * ear's predecessor vertex -- the old one moved to the new triangle, so any
49
+ * caller holding it must swap in this one
50
+ */
51
+ function cut_ear(mesh, face, l_ear, normal) {
52
+ const l_prev = mesh.loop_read_prev(l_ear);
53
+ const l_next = mesh.loop_read_next(l_ear);
54
+
55
+ const v_anchor = mesh.loop_read_vertex(l_prev);
56
+ const v_target = mesh.loop_read_vertex(l_next);
57
+
58
+ // handles allocation and vertex-disk linking for the diagonal
59
+ const e_new = bt_edge_create(mesh, v_anchor, v_target);
60
+
61
+ const f_new = mesh.faces.allocate();
62
+ const l_tri = mesh.loop_create(); // closes the triangle, at v_target
63
+ const l_poly = mesh.loop_create(); // continues the polygon, at v_anchor
64
+
65
+ mesh.loop_write_vertex(l_tri, v_target);
66
+ mesh.loop_write_face(l_tri, f_new);
67
+
68
+ mesh.loop_write_vertex(l_poly, v_anchor);
69
+ mesh.loop_write_face(l_poly, face);
70
+
71
+ bt_radial_loop_add(mesh, l_tri, e_new);
72
+ bt_radial_loop_add(mesh, l_poly, e_new);
73
+
74
+ const l_prev_prev = mesh.loop_read_prev(l_prev);
75
+
76
+ mesh.loop_write_face(l_prev, f_new);
77
+ mesh.loop_write_face(l_ear, f_new);
78
+
79
+ // triangle: l_prev -> l_ear -> l_tri
80
+ mesh.loop_write_next(l_prev, l_ear);
81
+ mesh.loop_write_prev(l_prev, l_tri);
82
+
83
+ mesh.loop_write_next(l_ear, l_tri);
84
+ mesh.loop_write_prev(l_ear, l_prev);
85
+
86
+ mesh.loop_write_next(l_tri, l_prev);
87
+ mesh.loop_write_prev(l_tri, l_ear);
88
+
89
+ mesh.face_write_loop(f_new, l_prev);
90
+ mesh.face_write_normal(f_new, normal, 0);
91
+
92
+ // remaining polygon: l_prev_prev -> l_poly -> l_next
93
+ mesh.loop_write_next(l_prev_prev, l_poly);
94
+
95
+ mesh.loop_write_prev(l_poly, l_prev_prev);
96
+ mesh.loop_write_next(l_poly, l_next);
97
+
98
+ mesh.loop_write_prev(l_next, l_poly);
99
+
100
+ mesh.face_write_loop(face, l_poly);
101
+
102
+ return l_poly;
103
+ }
104
+
105
+ /**
106
+ * Reset the corner index ring to `0 -> 1 -> ... -> corner_count - 1 -> 0`, the
107
+ * order the corners were collected in.
108
+ *
109
+ * @param {number} corner_count
110
+ */
111
+ function reset_ring(corner_count) {
112
+ for (let i = 0; i < corner_count; i++) {
113
+ _ring_next[i] = (i + 1) % corner_count;
114
+ _ring_prev[i] = (i + corner_count - 1) % corner_count;
115
+ }
116
+ }
117
+
118
+ /**
119
+ * Remove index `i` from the corner ring.
120
+ *
121
+ * @param {number} i
122
+ */
123
+ function unlink_ring(i) {
124
+ _ring_next[_ring_prev[i]] = _ring_next[i];
125
+ _ring_prev[_ring_next[i]] = _ring_prev[i];
126
+ }
127
+
128
+ /**
129
+ * Replay the emitted triangle list against a plain index ring without touching the
130
+ * mesh, checking that every triple is an ear of the ring at the moment it is
131
+ * reached. Only then is it safe to perform the same sequence as topology surgery.
132
+ *
133
+ * Ear clipping emits exactly this sequence for a simple polygon. Its recovery
134
+ * passes, which run for a self-intersecting projection, do not -- and half-applying
135
+ * those would leave the mesh structurally broken rather than merely untriangulated.
136
+ *
137
+ * @param {number} corner_count
138
+ * @param {number} triangle_count
139
+ * @returns {boolean}
140
+ */
141
+ function is_ear_sequence(corner_count, triangle_count) {
142
+ if (triangle_count !== corner_count - 2) {
143
+ return false;
144
+ }
145
+
146
+ reset_ring(corner_count);
147
+
148
+ for (let t = 0; t < triangle_count; t++) {
149
+ const a = _triangles[t * 3];
150
+ const b = _triangles[t * 3 + 1];
151
+ const c = _triangles[t * 3 + 2];
152
+
153
+ const forward = _ring_prev[b] === a && _ring_next[b] === c;
154
+ const backward = _ring_prev[b] === c && _ring_next[b] === a;
155
+
156
+ if (!forward && !backward) {
157
+ return false;
158
+ }
159
+
160
+ unlink_ring(b);
161
+ }
162
+
163
+ return true;
164
+ }
165
+
166
+ /**
167
+ * Split one face of a {@link BinaryTopology} into triangles, in place.
168
+ *
169
+ * Handles any simple polygon, convex or not: the corner order is projected onto
170
+ * the plane of the face's own Newell normal and triangulated by
171
+ * {@link polygon3_triangulate}, whose decisions are exact. The resulting ear
172
+ * sequence is then replayed as topology surgery, so existing corners keep their
173
+ * identity (and any per-loop data) and only the diagonals are new. Each new face
174
+ * inherits the original's stored normal.
175
+ *
176
+ * Superseded the previous fan-from-the-first-corner implementation, which was
177
+ * correct only for convex faces and produced silently overlapping triangles for
178
+ * anything else.
179
+ *
180
+ * Nothing is changed, and `1` is returned, when the face is already a triangle,
181
+ * has no area (so no plane to project onto), or projects to a self-intersecting
182
+ * outline -- which is a non-planar or otherwise malformed face, and cannot be cut
183
+ * into ears at all. Leaving it whole is the honest outcome; half-cutting it would
184
+ * corrupt the mesh.
185
+ *
186
+ * @param {BinaryTopology} mesh
187
+ * @param {number} face
188
+ * @returns {number} how many faces the input face became: `1` when it was left
189
+ * untouched, otherwise the number of triangles it was split into
190
+ *
191
+ * @author Alex Goldring
192
+ * @copyright Company Named Limited (c) 2026
193
+ */
194
+ export function bt_face_triangulate(mesh, face) {
195
+ const first_loop = mesh.face_read_loop(face);
196
+
197
+ if (first_loop === NULL_POINTER) {
198
+ return 1;
199
+ }
200
+
201
+ let corner_count = 0;
202
+ let iter = first_loop;
203
+
204
+ do {
205
+ corner_count++;
206
+ iter = mesh.loop_read_next(iter);
207
+ } while (iter !== first_loop);
208
+
209
+ if (corner_count < 4) {
210
+ return 1;
211
+ }
212
+
213
+ ensure_capacity(corner_count);
214
+
215
+ iter = first_loop;
216
+
217
+ for (let i = 0; i < corner_count; i++) {
218
+ _loops[i] = iter;
219
+
220
+ mesh.vertex_read_coordinate(_coordinate, 0, mesh.loop_read_vertex(iter));
221
+
222
+ _positions[i * 3] = _coordinate[0];
223
+ _positions[i * 3 + 1] = _coordinate[1];
224
+ _positions[i * 3 + 2] = _coordinate[2];
225
+
226
+ iter = mesh.loop_read_next(iter);
227
+ }
228
+
229
+ polygon3_compute_normal(_normal, 0, _positions, 0, corner_count);
230
+
231
+ if (_normal[0] === 0 && _normal[1] === 0 && _normal[2] === 0) {
232
+ // no area, so no plane to project onto
233
+ return 1;
234
+ }
235
+
236
+ _contour_ends[0] = corner_count;
237
+
238
+ const triangle_count = polygon3_triangulate(
239
+ _triangles, 0,
240
+ _positions, _contour_ends, 1,
241
+ _normal[0], _normal[1], _normal[2]
242
+ );
243
+
244
+ if (!is_ear_sequence(corner_count, triangle_count)) {
245
+ return 1;
246
+ }
247
+
248
+ mesh.face_read_normal(_stored_normal, 0, face);
249
+
250
+ // The last triple is the residual triangle, which is what `face` itself
251
+ // becomes; the other n-3 are cut off it one at a time.
252
+ //
253
+ // The ring is re-walked alongside the cuts because each cut hands the shrunken
254
+ // polygon a NEW corner in place of the ear's predecessor -- `_loops` has to
255
+ // follow that swap or the next cut would reach into the triangle just made.
256
+ reset_ring(corner_count);
257
+
258
+ for (let t = 0; t < triangle_count - 1; t++) {
259
+ const ear = _triangles[t * 3 + 1];
260
+ const predecessor = _ring_prev[ear];
261
+
262
+ _loops[predecessor] = cut_ear(mesh, face, _loops[ear], _stored_normal);
263
+
264
+ unlink_ring(ear);
265
+ }
266
+
267
+ return triangle_count;
268
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Run {@link bt_mesh_validate} and throw if the mesh is structurally broken.
3
+ *
4
+ * Uses a real `throw` rather than `assert.*` on purpose: `assert.*` is stripped
5
+ * from production bundles, and this is the check a spec relies on to fail. A
6
+ * caller that wants the free-in-production behaviour should call
7
+ * {@link bt_mesh_validate} inside its own `assert.ok(...)`.
8
+ *
9
+ * @param {BinaryTopology} mesh
10
+ * @param {string} [context] prepended to the message, e.g. the operation under test
11
+ * @throws {Error} when any structural invariant is broken
12
+ *
13
+ * @author Alex Goldring
14
+ * @copyright Company Named Limited (c) 2026
15
+ */
16
+ export function bt_mesh_assert_valid(mesh: BinaryTopology, context?: string): void;
17
+ //# sourceMappingURL=bt_mesh_assert_valid.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bt_mesh_assert_valid.d.ts","sourceRoot":"","sources":["../../../../../../../../../src/core/geom/3d/topology/struct/binary/query/bt_mesh_assert_valid.js"],"names":[],"mappings":"AAUA;;;;;;;;;;;;;;GAcG;AACH,qEANW,MAAM,QAwBhB"}
@@ -0,0 +1,44 @@
1
+ import { bt_mesh_validate } from "./bt_mesh_validate.js";
2
+
3
+ /**
4
+ * Maximum number of defects quoted in the thrown message. A corrupted cycle
5
+ * typically reports one defect per element on it, and a 10k-element dump helps
6
+ * nobody read the first one.
7
+ * @type {number}
8
+ */
9
+ const REPORTED_DEFECT_LIMIT = 20;
10
+
11
+ /**
12
+ * Run {@link bt_mesh_validate} and throw if the mesh is structurally broken.
13
+ *
14
+ * Uses a real `throw` rather than `assert.*` on purpose: `assert.*` is stripped
15
+ * from production bundles, and this is the check a spec relies on to fail. A
16
+ * caller that wants the free-in-production behaviour should call
17
+ * {@link bt_mesh_validate} inside its own `assert.ok(...)`.
18
+ *
19
+ * @param {BinaryTopology} mesh
20
+ * @param {string} [context] prepended to the message, e.g. the operation under test
21
+ * @throws {Error} when any structural invariant is broken
22
+ *
23
+ * @author Alex Goldring
24
+ * @copyright Company Named Limited (c) 2026
25
+ */
26
+ export function bt_mesh_assert_valid(mesh, context = "") {
27
+ const defects = bt_mesh_validate(mesh);
28
+
29
+ if (defects.length === 0) {
30
+ return;
31
+ }
32
+
33
+ const quoted = defects.slice(0, REPORTED_DEFECT_LIMIT);
34
+
35
+ const lines = quoted.map(d => ` [code ${d.code}] ${d.detail}`);
36
+
37
+ if (defects.length > quoted.length) {
38
+ lines.push(` ... and ${defects.length - quoted.length} more`);
39
+ }
40
+
41
+ const prefix = context === "" ? "" : `${context}: `;
42
+
43
+ throw new Error(`${prefix}mesh has ${defects.length} structural defect(s)\n${lines.join("\n")}`);
44
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Number of loops on an edge's radial cycle, i.e. how many faces use that edge.
3
+ *
4
+ * `0` is a wire edge, `1` a boundary edge, `2` a manifold interior edge, and `3`
5
+ * or more a non-manifold edge. {@link bt_edge_is_boundary} answers the `1` case
6
+ * on its own; this is the counting version the manifoldness queries need.
7
+ *
8
+ * The walk is bounded by the loop pool's size so a corrupted radial cycle cannot
9
+ * hang the caller. A cycle that long is itself broken -- run
10
+ * {@link bt_mesh_validate} to find out where.
11
+ *
12
+ * @param {BinaryTopology} mesh
13
+ * @param {number} edge_id
14
+ * @returns {number}
15
+ *
16
+ * @author Alex Goldring
17
+ * @copyright Company Named Limited (c) 2026
18
+ */
19
+ export function bt_mesh_count_edge_faces(mesh: BinaryTopology, edge_id: number): number;
20
+ //# sourceMappingURL=bt_mesh_count_edge_faces.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bt_mesh_count_edge_faces.d.ts","sourceRoot":"","sources":["../../../../../../../../../src/core/geom/3d/topology/struct/binary/query/bt_mesh_count_edge_faces.js"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;GAiBG;AACH,wEANW,MAAM,GACJ,MAAM,CAwBlB"}
@@ -0,0 +1,40 @@
1
+ import { NULL_POINTER } from "../BinaryTopology.js";
2
+
3
+ /**
4
+ * Number of loops on an edge's radial cycle, i.e. how many faces use that edge.
5
+ *
6
+ * `0` is a wire edge, `1` a boundary edge, `2` a manifold interior edge, and `3`
7
+ * or more a non-manifold edge. {@link bt_edge_is_boundary} answers the `1` case
8
+ * on its own; this is the counting version the manifoldness queries need.
9
+ *
10
+ * The walk is bounded by the loop pool's size so a corrupted radial cycle cannot
11
+ * hang the caller. A cycle that long is itself broken -- run
12
+ * {@link bt_mesh_validate} to find out where.
13
+ *
14
+ * @param {BinaryTopology} mesh
15
+ * @param {number} edge_id
16
+ * @returns {number}
17
+ *
18
+ * @author Alex Goldring
19
+ * @copyright Company Named Limited (c) 2026
20
+ */
21
+ export function bt_mesh_count_edge_faces(mesh, edge_id) {
22
+ const start = mesh.edge_read_loop(edge_id);
23
+
24
+ if (start === NULL_POINTER) {
25
+ return 0;
26
+ }
27
+
28
+ const limit = mesh.loops.size;
29
+
30
+ let l = start;
31
+ let count = 0;
32
+
33
+ do {
34
+ count++;
35
+
36
+ l = mesh.loop_read_radial_next(l);
37
+ } while (l !== start && l !== NULL_POINTER && count <= limit);
38
+
39
+ return count;
40
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * True when every edge is used by exactly two faces -- a watertight surface with
3
+ * no boundary, no wire edges and no non-manifold edges.
4
+ *
5
+ * This is what "is this a solid?" means for volume, genus and inside/outside
6
+ * queries: {@link tri3_mesh_compute_signed_volume} is only meaningful on a closed
7
+ * mesh, and {@link bt_mesh_close_boundary_holes} exists to make an open one
8
+ * closed.
9
+ *
10
+ * An empty mesh is closed, vacuously -- there is no edge to violate the rule.
11
+ *
12
+ * @param {BinaryTopology} mesh
13
+ * @returns {boolean}
14
+ *
15
+ * @author Alex Goldring
16
+ * @copyright Company Named Limited (c) 2026
17
+ */
18
+ export function bt_mesh_is_closed(mesh: BinaryTopology): boolean;
19
+ //# sourceMappingURL=bt_mesh_is_closed.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bt_mesh_is_closed.d.ts","sourceRoot":"","sources":["../../../../../../../../../src/core/geom/3d/topology/struct/binary/query/bt_mesh_is_closed.js"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;GAgBG;AACH,yDALa,OAAO,CAmBnB"}