@woosh/meep-engine 2.164.0 → 2.165.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 (256) hide show
  1. package/build/bundle-worker-terrain.js +1 -1
  2. package/package.json +1 -1
  3. package/src/core/binary/BitImage2.d.ts +91 -0
  4. package/src/core/binary/BitImage2.d.ts.map +1 -0
  5. package/src/core/binary/BitImage2.js +242 -0
  6. package/src/core/geom/2d/oriented-box/min_area_rect_2d.d.ts +22 -0
  7. package/src/core/geom/2d/oriented-box/min_area_rect_2d.d.ts.map +1 -0
  8. package/src/core/geom/2d/oriented-box/min_area_rect_2d.js +69 -0
  9. package/src/core/geom/2d/packing/skyline.d.ts +116 -0
  10. package/src/core/geom/2d/packing/skyline.d.ts.map +1 -0
  11. package/src/core/geom/2d/packing/skyline.js +258 -0
  12. package/src/core/geom/2d/triangle/tri2_rasterize_conservative.d.ts +18 -0
  13. package/src/core/geom/2d/triangle/tri2_rasterize_conservative.d.ts.map +1 -0
  14. package/src/core/geom/2d/triangle/tri2_rasterize_conservative.js +86 -0
  15. package/src/core/geom/3d/atlas/ATLAS_PORT_PLAN.md +486 -0
  16. package/src/core/geom/3d/atlas/BFF_PLAN.md +174 -0
  17. package/src/core/geom/3d/atlas/Chart.d.ts +58 -0
  18. package/src/core/geom/3d/atlas/Chart.d.ts.map +1 -0
  19. package/src/core/geom/3d/atlas/Chart.js +77 -0
  20. package/src/core/geom/3d/atlas/REVIEW_02.md +392 -0
  21. package/src/core/geom/3d/atlas/atlas_bench_lib.d.ts +73 -0
  22. package/src/core/geom/3d/atlas/atlas_bench_lib.d.ts.map +1 -0
  23. package/src/core/geom/3d/atlas/atlas_bench_lib.js +315 -0
  24. package/src/core/geom/3d/atlas/atlas_chart_type.d.ts +17 -0
  25. package/src/core/geom/3d/atlas/atlas_chart_type.d.ts.map +1 -0
  26. package/src/core/geom/3d/atlas/atlas_chart_type.js +16 -0
  27. package/src/core/geom/3d/atlas/atlas_compute_charts.d.ts +25 -0
  28. package/src/core/geom/3d/atlas/atlas_compute_charts.d.ts.map +1 -0
  29. package/src/core/geom/3d/atlas/atlas_compute_charts.js +188 -0
  30. package/src/core/geom/3d/atlas/atlas_extract_chart.d.ts +17 -0
  31. package/src/core/geom/3d/atlas/atlas_extract_chart.d.ts.map +1 -0
  32. package/src/core/geom/3d/atlas/atlas_extract_chart.js +107 -0
  33. package/src/core/geom/3d/atlas/atlas_generate.d.ts +39 -0
  34. package/src/core/geom/3d/atlas/atlas_generate.d.ts.map +1 -0
  35. package/src/core/geom/3d/atlas/atlas_generate.js +43 -0
  36. package/src/core/geom/3d/atlas/atlas_test_fixtures.d.ts +84 -0
  37. package/src/core/geom/3d/atlas/atlas_test_fixtures.d.ts.map +1 -0
  38. package/src/core/geom/3d/atlas/atlas_test_fixtures.js +266 -0
  39. package/src/core/geom/3d/atlas/atlas_write_chart_uvs.d.ts +25 -0
  40. package/src/core/geom/3d/atlas/atlas_write_chart_uvs.d.ts.map +1 -0
  41. package/src/core/geom/3d/atlas/atlas_write_chart_uvs.js +36 -0
  42. package/src/core/geom/3d/atlas/data/atlas_build_edge_dihedrals.d.ts +22 -0
  43. package/src/core/geom/3d/atlas/data/atlas_build_edge_dihedrals.d.ts.map +1 -0
  44. package/src/core/geom/3d/atlas/data/atlas_build_edge_dihedrals.js +62 -0
  45. package/src/core/geom/3d/atlas/data/atlas_build_edge_lengths.d.ts +9 -0
  46. package/src/core/geom/3d/atlas/data/atlas_build_edge_lengths.d.ts.map +1 -0
  47. package/src/core/geom/3d/atlas/data/atlas_build_edge_lengths.js +33 -0
  48. package/src/core/geom/3d/atlas/data/atlas_build_face_areas.d.ts +9 -0
  49. package/src/core/geom/3d/atlas/data/atlas_build_face_areas.d.ts.map +1 -0
  50. package/src/core/geom/3d/atlas/data/atlas_build_face_areas.js +50 -0
  51. package/src/core/geom/3d/atlas/io/atlas_mesh_weld_colocals.d.ts +27 -0
  52. package/src/core/geom/3d/atlas/io/atlas_mesh_weld_colocals.d.ts.map +1 -0
  53. package/src/core/geom/3d/atlas/io/atlas_mesh_weld_colocals.js +39 -0
  54. package/src/core/geom/3d/atlas/io/bt_loop_uv.d.ts +22 -0
  55. package/src/core/geom/3d/atlas/io/bt_loop_uv.d.ts.map +1 -0
  56. package/src/core/geom/3d/atlas/io/bt_loop_uv.js +57 -0
  57. package/src/core/geom/3d/atlas/pack/ChartPlacement.d.ts +36 -0
  58. package/src/core/geom/3d/atlas/pack/ChartPlacement.d.ts.map +1 -0
  59. package/src/core/geom/3d/atlas/pack/ChartPlacement.js +46 -0
  60. package/src/core/geom/3d/atlas/pack/atlas_pack_charts.d.ts +62 -0
  61. package/src/core/geom/3d/atlas/pack/atlas_pack_charts.d.ts.map +1 -0
  62. package/src/core/geom/3d/atlas/pack/atlas_pack_charts.js +551 -0
  63. package/src/core/geom/3d/atlas/param/atlas_chart_param_validity.d.ts +22 -0
  64. package/src/core/geom/3d/atlas/param/atlas_chart_param_validity.d.ts.map +1 -0
  65. package/src/core/geom/3d/atlas/param/atlas_chart_param_validity.js +73 -0
  66. package/src/core/geom/3d/atlas/param/atlas_chart_parameterize_bff.d.ts +37 -0
  67. package/src/core/geom/3d/atlas/param/atlas_chart_parameterize_bff.d.ts.map +1 -0
  68. package/src/core/geom/3d/atlas/param/atlas_chart_parameterize_bff.js +270 -0
  69. package/src/core/geom/3d/atlas/param/atlas_chart_parameterize_lscm.d.ts +23 -0
  70. package/src/core/geom/3d/atlas/param/atlas_chart_parameterize_lscm.d.ts.map +1 -0
  71. package/src/core/geom/3d/atlas/param/atlas_chart_parameterize_lscm.js +224 -0
  72. package/src/core/geom/3d/atlas/param/atlas_chart_parameterize_ortho.d.ts +14 -0
  73. package/src/core/geom/3d/atlas/param/atlas_chart_parameterize_ortho.d.ts.map +1 -0
  74. package/src/core/geom/3d/atlas/param/atlas_chart_parameterize_ortho.js +38 -0
  75. package/src/core/geom/3d/atlas/param/atlas_chart_stretch_metric.d.ts +22 -0
  76. package/src/core/geom/3d/atlas/param/atlas_chart_stretch_metric.d.ts.map +1 -0
  77. package/src/core/geom/3d/atlas/param/atlas_chart_stretch_metric.js +86 -0
  78. package/src/core/geom/3d/atlas/param/atlas_piecewise_resegment.d.ts +20 -0
  79. package/src/core/geom/3d/atlas/param/atlas_piecewise_resegment.d.ts.map +1 -0
  80. package/src/core/geom/3d/atlas/param/atlas_piecewise_resegment.js +183 -0
  81. package/src/core/geom/3d/atlas/param/fit_plane_basis.d.ts +27 -0
  82. package/src/core/geom/3d/atlas/param/fit_plane_basis.d.ts.map +1 -0
  83. package/src/core/geom/3d/atlas/param/fit_plane_basis.js +66 -0
  84. package/src/core/geom/3d/atlas/segment/RegionGraph.d.ts +31 -0
  85. package/src/core/geom/3d/atlas/segment/RegionGraph.d.ts.map +1 -0
  86. package/src/core/geom/3d/atlas/segment/RegionGraph.js +40 -0
  87. package/src/core/geom/3d/atlas/segment/atlas_build_region_graph.d.ts +23 -0
  88. package/src/core/geom/3d/atlas/segment/atlas_build_region_graph.d.ts.map +1 -0
  89. package/src/core/geom/3d/atlas/segment/atlas_build_region_graph.js +136 -0
  90. package/src/core/geom/3d/atlas/segment/atlas_compute_planar_regions.d.ts +24 -0
  91. package/src/core/geom/3d/atlas/segment/atlas_compute_planar_regions.d.ts.map +1 -0
  92. package/src/core/geom/3d/atlas/segment/atlas_compute_planar_regions.js +77 -0
  93. package/src/core/geom/3d/atlas/segment/atlas_merge_charts.d.ts +27 -0
  94. package/src/core/geom/3d/atlas/segment/atlas_merge_charts.d.ts.map +1 -0
  95. package/src/core/geom/3d/atlas/segment/atlas_merge_charts.js +113 -0
  96. package/src/core/geom/3d/atlas/segment/atlas_segment_charts.d.ts +44 -0
  97. package/src/core/geom/3d/atlas/segment/atlas_segment_charts.d.ts.map +1 -0
  98. package/src/core/geom/3d/atlas/segment/atlas_segment_charts.js +275 -0
  99. package/src/core/geom/3d/atlas/segment/atlas_segment_charts_atomic.d.ts +22 -0
  100. package/src/core/geom/3d/atlas/segment/atlas_segment_charts_atomic.d.ts.map +1 -0
  101. package/src/core/geom/3d/atlas/segment/atlas_segment_charts_atomic.js +199 -0
  102. package/src/core/geom/3d/line/line3_compute_point_segment_point_distance_unfolded.d.ts +32 -0
  103. package/src/core/geom/3d/line/line3_compute_point_segment_point_distance_unfolded.d.ts.map +1 -0
  104. package/src/core/geom/3d/line/line3_compute_point_segment_point_distance_unfolded.js +64 -0
  105. package/src/core/geom/3d/mat4/m4_from_rotation_translation_scale.d.ts +20 -0
  106. package/src/core/geom/3d/mat4/m4_from_rotation_translation_scale.d.ts.map +1 -0
  107. package/src/core/geom/3d/mat4/m4_from_rotation_translation_scale.js +66 -0
  108. package/src/core/geom/3d/sphere/harmonics/sh3_dering_optimize_positive.d.ts.map +1 -1
  109. package/src/core/geom/3d/sphere/harmonics/sh3_dering_optimize_positive.js +13 -11
  110. package/src/core/geom/3d/sphere/sphere_compute_bounding_of_2_spheres.d.ts.map +1 -1
  111. package/src/core/geom/3d/sphere/sphere_compute_bounding_of_2_spheres.js +4 -3
  112. package/src/core/geom/3d/topology/simplify/decimate_edge_collapse_snap.d.ts.map +1 -1
  113. package/src/core/geom/3d/topology/simplify/decimate_edge_collapse_snap.js +4 -3
  114. package/src/core/geom/3d/topology/simplify/quadratic/Quadric3.d.ts.map +1 -1
  115. package/src/core/geom/3d/topology/simplify/quadratic/Quadric3.js +4 -3
  116. package/src/core/geom/3d/topology/simplify/quadratic/apply_border_penalty_to_vertex_quadrics.d.ts.map +1 -1
  117. package/src/core/geom/3d/topology/simplify/quadratic/apply_border_penalty_to_vertex_quadrics.js +10 -6
  118. package/src/core/geom/3d/topology/struct/TopoMesh.js +2 -2
  119. package/src/core/geom/3d/topology/struct/TopoTriangle.d.ts +0 -2
  120. package/src/core/geom/3d/topology/struct/TopoTriangle.d.ts.map +1 -1
  121. package/src/core/geom/3d/topology/struct/TopoTriangle.js +2 -2
  122. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_face_find_path_polyanya.d.ts.map +1 -1
  123. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_face_find_path_polyanya.js +8 -27
  124. package/src/core/geom/vec3/v3_cotangent.d.ts +20 -0
  125. package/src/core/geom/vec3/v3_cotangent.d.ts.map +1 -0
  126. package/src/core/geom/vec3/v3_cotangent.js +29 -0
  127. package/src/core/geom/vec3/v3_matrix3_multiply.d.ts +19 -0
  128. package/src/core/geom/vec3/v3_matrix3_multiply.d.ts.map +1 -0
  129. package/src/core/geom/vec3/v3_matrix3_multiply.js +30 -0
  130. package/src/core/geom/vec3/v3_multiply.d.ts +16 -0
  131. package/src/core/geom/vec3/v3_multiply.d.ts.map +1 -0
  132. package/src/core/geom/vec3/v3_multiply.js +19 -0
  133. package/src/core/math/linalg/cg/cg_solve.d.ts +24 -0
  134. package/src/core/math/linalg/cg/cg_solve.d.ts.map +1 -0
  135. package/src/core/math/linalg/cg/cg_solve.js +106 -0
  136. package/src/core/math/linalg/cg/cg_solve_normal_equations.d.ts +21 -0
  137. package/src/core/math/linalg/cg/cg_solve_normal_equations.d.ts.map +1 -0
  138. package/src/core/math/linalg/cg/cg_solve_normal_equations.js +59 -0
  139. package/src/core/math/linalg/eigen/matrix_symmetric_3x3_eigen.d.ts +19 -0
  140. package/src/core/math/linalg/eigen/matrix_symmetric_3x3_eigen.d.ts.map +1 -0
  141. package/src/core/math/linalg/eigen/matrix_symmetric_3x3_eigen.js +117 -0
  142. package/src/core/math/linalg/sparse/SparseMatrixCSR.d.ts +50 -0
  143. package/src/core/math/linalg/sparse/SparseMatrixCSR.d.ts.map +1 -0
  144. package/src/core/math/linalg/sparse/SparseMatrixCSR.js +88 -0
  145. package/src/core/math/linalg/sparse/spm_matvec.d.ts +9 -0
  146. package/src/core/math/linalg/sparse/spm_matvec.d.ts.map +1 -0
  147. package/src/core/math/linalg/sparse/spm_matvec.js +22 -0
  148. package/src/core/math/linalg/sparse/spm_matvec_transpose.d.ts +12 -0
  149. package/src/core/math/linalg/sparse/spm_matvec_transpose.d.ts.map +1 -0
  150. package/src/core/math/linalg/sparse/spm_matvec_transpose.js +29 -0
  151. package/src/core/math/physics/mie/MIE_PARTICLES_STANDARD_PRECOMPUTED.d.ts +39 -0
  152. package/src/core/math/physics/mie/MIE_PARTICLES_STANDARD_PRECOMPUTED.js +22 -1
  153. package/src/core/math/physics/mie/compute_mie_particle_properties_rgb.d.ts.map +1 -1
  154. package/src/core/math/physics/mie/compute_mie_particle_properties_rgb.js +32 -9
  155. package/src/core/math/physics/mie/compute_mie_phase.d.ts +2 -3
  156. package/src/core/math/physics/mie/compute_mie_phase.d.ts.map +1 -1
  157. package/src/core/math/physics/mie/compute_mie_phase.js +1 -3
  158. package/src/core/math/physics/mie/lorenz_mie_coefs.d.ts +0 -1
  159. package/src/core/math/physics/mie/lorenz_mie_coefs.d.ts.map +1 -1
  160. package/src/core/math/physics/mie/lorenz_mie_coefs.js +0 -7
  161. package/src/engine/ecs/attachment/TransformAttachmentBinding.js +3 -3
  162. package/src/engine/ecs/gui/hud/HeadsUpDisplaySystem.js +2 -2
  163. package/src/engine/ecs/terrain/ecs/Terrain.d.ts.map +1 -1
  164. package/src/engine/ecs/terrain/ecs/Terrain.js +4 -3
  165. package/src/engine/ecs/terrain/tiles/TerrainTile.js +2 -2
  166. package/src/engine/ecs/terrain/tiles/TerrainTileManager.d.ts.map +1 -1
  167. package/src/engine/ecs/terrain/tiles/TerrainTileManager.js +9 -8
  168. package/src/engine/graphics/GraphicsEngine.d.ts.map +1 -1
  169. package/src/engine/graphics/GraphicsEngine.js +2 -1
  170. package/src/engine/graphics/ecs/decal/v2/FPDecalSystem.js +2 -2
  171. package/src/engine/graphics/ecs/decal/v2/prototypeDecalSystem.js +2 -2
  172. package/src/engine/graphics/ecs/mesh-v2/ShadedGeometry.js +2 -2
  173. package/src/engine/graphics/ecs/mesh-v2/ShadedGeometrySystem.d.ts.map +1 -1
  174. package/src/engine/graphics/ecs/mesh-v2/ShadedGeometrySystem.js +0 -1
  175. package/src/engine/graphics/ecs/path/tube/build/computeFrenetFrames.d.ts.map +1 -1
  176. package/src/engine/graphics/ecs/path/tube/build/computeFrenetFrames.js +8 -7
  177. package/src/engine/graphics/geometry/MikkT/AvgTSpace.d.ts.map +1 -1
  178. package/src/engine/graphics/geometry/MikkT/AvgTSpace.js +11 -6
  179. package/src/engine/graphics/geometry/MikkT/BuildNeighborsFast.d.ts.map +1 -1
  180. package/src/engine/graphics/geometry/MikkT/BuildNeighborsFast.js +2 -3
  181. package/src/engine/graphics/geometry/MikkT/CalcTexArea.d.ts.map +1 -1
  182. package/src/engine/graphics/geometry/MikkT/CalcTexArea.js +3 -4
  183. package/src/engine/graphics/geometry/MikkT/DegenEpilogue.js +4 -4
  184. package/src/engine/graphics/geometry/MikkT/EvalTspace.d.ts.map +1 -1
  185. package/src/engine/graphics/geometry/MikkT/EvalTspace.js +11 -8
  186. package/src/engine/graphics/geometry/MikkT/GenerateSharedVerticesIndexList.d.ts.map +1 -1
  187. package/src/engine/graphics/geometry/MikkT/GenerateSharedVerticesIndexList.js +7 -8
  188. package/src/engine/graphics/geometry/MikkT/GenerateTSpaces.js +8 -8
  189. package/src/engine/graphics/geometry/MikkT/InitTriInfo.d.ts.map +1 -1
  190. package/src/engine/graphics/geometry/MikkT/InitTriInfo.js +30 -25
  191. package/src/engine/graphics/geometry/MikkT/MikkTSpace.js +7 -7
  192. package/src/engine/graphics/geometry/MikkT/NormalizeSafe.d.ts +0 -1
  193. package/src/engine/graphics/geometry/MikkT/NormalizeSafe.d.ts.map +1 -1
  194. package/src/engine/graphics/geometry/MikkT/NormalizeSafe.js +6 -4
  195. package/src/engine/graphics/geometry/MikkT/STSpace.d.ts +0 -2
  196. package/src/engine/graphics/geometry/MikkT/STSpace.d.ts.map +1 -1
  197. package/src/engine/graphics/geometry/MikkT/STSpace.js +3 -3
  198. package/src/engine/graphics/geometry/MikkT/v3_scale_dot_sub_normalize.d.ts +0 -2
  199. package/src/engine/graphics/geometry/MikkT/v3_scale_dot_sub_normalize.d.ts.map +1 -1
  200. package/src/engine/graphics/geometry/MikkT/v3_scale_dot_sub_normalize.js +0 -1
  201. package/src/engine/graphics/impostors/octahedral/ImpostorBaker.d.ts +0 -2
  202. package/src/engine/graphics/impostors/octahedral/ImpostorBaker.d.ts.map +1 -1
  203. package/src/engine/graphics/impostors/octahedral/ImpostorBaker.js +4 -3
  204. package/src/engine/graphics/impostors/octahedral/grid/HemiOctahedralUvEncoder.js +2 -2
  205. package/src/engine/graphics/impostors/octahedral/grid/UvEncoder.d.ts +17 -7
  206. package/src/engine/graphics/impostors/voxel/VoxelImpostorBaker.js +2 -2
  207. package/src/engine/graphics/particles/particular/engine/utils/volume/ParticleVolume.js +3 -3
  208. package/src/engine/graphics/particles/particular/engine/utils/volume/prototypeParticleVolume.js +4 -3
  209. package/src/engine/graphics/render/forward_plus/LightManager.js +2 -2
  210. package/src/engine/graphics/render/forward_plus/model/Decal.d.ts +0 -2
  211. package/src/engine/graphics/render/forward_plus/model/Decal.d.ts.map +1 -1
  212. package/src/engine/graphics/render/forward_plus/model/Decal.js +3 -3
  213. package/src/engine/graphics/render/forward_plus/prototype/prototypeLightManager.js +4 -3
  214. package/src/engine/graphics/render/visibility/hiz/prototypeHiZ.js +6 -4
  215. package/src/engine/graphics/sh3/lightmap/ARTIFACTS_PLAN.md +146 -0
  216. package/src/engine/graphics/sh3/lightmap/CODE_REVIEW.md +121 -0
  217. package/src/engine/graphics/sh3/lightmap/LIGHTMAPPER_REVIEW.md +183 -0
  218. package/src/engine/graphics/sh3/lightmap/bake_lightmap_for_scene.d.ts +90 -0
  219. package/src/engine/graphics/sh3/lightmap/bake_lightmap_for_scene.d.ts.map +1 -0
  220. package/src/engine/graphics/sh3/lightmap/bake_lightmap_for_scene.js +678 -0
  221. package/src/engine/graphics/sh3/lightmap/lightmap_build_geometry.d.ts +32 -0
  222. package/src/engine/graphics/sh3/lightmap/lightmap_build_geometry.d.ts.map +1 -0
  223. package/src/engine/graphics/sh3/lightmap/lightmap_build_geometry.js +155 -0
  224. package/src/engine/graphics/sh3/lightmap/lightmap_denoise.d.ts +49 -0
  225. package/src/engine/graphics/sh3/lightmap/lightmap_denoise.d.ts.map +1 -0
  226. package/src/engine/graphics/sh3/lightmap/lightmap_denoise.js +136 -0
  227. package/src/engine/graphics/sh3/lightmap/lightmap_dilate.d.ts +19 -0
  228. package/src/engine/graphics/sh3/lightmap/lightmap_dilate.d.ts.map +1 -0
  229. package/src/engine/graphics/sh3/lightmap/lightmap_dilate.js +89 -0
  230. package/src/engine/graphics/sh3/lightmap/lightmap_gather.d.ts +54 -0
  231. package/src/engine/graphics/sh3/lightmap/lightmap_gather.d.ts.map +1 -0
  232. package/src/engine/graphics/sh3/lightmap/lightmap_gather.js +168 -0
  233. package/src/engine/graphics/sh3/lightmap/lightmap_rasterize_triangle.d.ts +30 -0
  234. package/src/engine/graphics/sh3/lightmap/lightmap_rasterize_triangle.d.ts.map +1 -0
  235. package/src/engine/graphics/sh3/lightmap/lightmap_rasterize_triangle.js +145 -0
  236. package/src/engine/graphics/sh3/lightmap/lightmap_seam_fix.d.ts +38 -0
  237. package/src/engine/graphics/sh3/lightmap/lightmap_seam_fix.d.ts.map +1 -0
  238. package/src/engine/graphics/sh3/lightmap/lightmap_seam_fix.js +152 -0
  239. package/src/engine/graphics/sh3/lightmap/prototypeLightmapBaker.d.ts +2 -0
  240. package/src/engine/graphics/sh3/lightmap/prototypeLightmapBaker.d.ts.map +1 -0
  241. package/src/engine/graphics/sh3/lightmap/prototypeLightmapBaker.js +186 -0
  242. package/src/engine/graphics/sh3/path_tracer/PathTracedMesh.d.ts.map +1 -1
  243. package/src/engine/graphics/sh3/path_tracer/PathTracedMesh.js +2 -2
  244. package/src/engine/graphics/sh3/path_tracer/PathTracer.js +2 -2
  245. package/src/engine/graphics/sh3/path_tracer/prepare_scene_gltf.d.ts.map +1 -1
  246. package/src/engine/graphics/sh3/path_tracer/prepare_scene_gltf.js +6 -4
  247. package/src/engine/graphics/sh3/path_tracer/prototypePathTracer.js +3 -3
  248. package/src/engine/graphics/sh3/path_tracer/texture/sample_material.d.ts.map +1 -1
  249. package/src/engine/graphics/sh3/path_tracer/texture/sample_material.js +5 -4
  250. package/src/engine/graphics/sh3/sky/hosek/prototype_hosek.js +2 -2
  251. package/src/engine/input/devices/KeyboardDevice.d.ts +61 -14
  252. package/src/engine/input/devices/KeyboardDevice.d.ts.map +1 -1
  253. package/src/engine/input/devices/KeyboardDevice.js +41 -15
  254. package/src/engine/input/devices/isHTMLElementFocusable.d.ts.map +1 -1
  255. package/src/engine/input/devices/isHTMLElementFocusable.js +2 -9
  256. package/src/engine/sound/simulation/prototypeAcousticSolver.js +261 -143
@@ -0,0 +1,551 @@
1
+ import { BitImage2 } from "../../../../binary/BitImage2.js";
2
+ import { ceilPowerOfTwo } from "../../../../binary/operations/ceilPowerOfTwo.js";
3
+ import { min_area_rect_2d } from "../../../2d/oriented-box/min_area_rect_2d.js";
4
+ import { Skyline } from "../../../2d/packing/skyline.js";
5
+ import { tri2_rasterize_conservative } from "../../../2d/triangle/tri2_rasterize_conservative.js";
6
+ import { ChartPlacement } from "./ChartPlacement.js";
7
+
8
+ /** Target atlas utilization used when auto-estimating density. */
9
+ const UTILIZATION_TARGET = 0.75;
10
+
11
+ /** Upper bound on single-page growth — a runaway backstop, not a real limit. */
12
+ const MAX_ATLAS_SIZE = 1 << 16;
13
+
14
+ /** Elastic density: shrink factor per attempt, and the attempt cap. */
15
+ const ELASTIC_SHRINK = 0.8;
16
+ const MAX_ELASTIC_ATTEMPTS = 40;
17
+
18
+ /**
19
+ * The exact bitmap canBlit packer (tight, non-rectangular interlocking) is used
20
+ * only for atlases up to this many texels — its scan is O(area·mask). Above it,
21
+ * and unless `bruteForce` is forced, the fast O(n) skyline rectangle packer is
22
+ * used so packing scales to production atlas sizes.
23
+ */
24
+ const BRUTE_FORCE_AREA = 1 << 16; // 256 x 256
25
+
26
+ // ─── Shared ──────────────────────────────────────────────────────────────────
27
+ /**
28
+ * Rotate a chart's UVs so its minimum-area oriented box becomes axis-aligned
29
+ * (xatlas `rotateChartsToAxis`). Rigid, so validity/stretch are unchanged.
30
+ *
31
+ * @param {Chart} chart chart whose UVs are rotated in place
32
+ */
33
+ function rotate_uv_to_min_area_box(chart) {
34
+ const box = min_area_rect_2d(chart.uv, chart.vertex_count);
35
+ const c = Math.cos(box.angle);
36
+ const s = Math.sin(box.angle);
37
+ const uv = chart.uv;
38
+ for (let i = 0; i < chart.vertex_count; i++) {
39
+ const u = uv[i * 2];
40
+ const v = uv[i * 2 + 1];
41
+ uv[i * 2] = u * c + v * s;
42
+ uv[i * 2 + 1] = -u * s + v * c;
43
+ }
44
+ }
45
+
46
+ /**
47
+ * Per-chart placeable at a given texel density: scale + UV bbox + integer rect
48
+ * size (w, h, padding-inflated). With `need_mask`, also a dilated coverage mask
49
+ * (+ transpose) for the bitmap packer; skyline only needs w/h.
50
+ *
51
+ * @param {Chart[]} charts parameterized charts to build placeables from
52
+ * @param {number} density texels per unit of surface area
53
+ * @param {number} padding gutter texels added on each side
54
+ * @param {boolean} rotate_charts allow 90-degree placement (builds the transposed mask)
55
+ * @param {boolean} need_mask build per-chart coverage masks (bitmap packer) vs w/h only (skyline)
56
+ * @returns {{packs:ChartPlacement[], mask_total:number}} placements and the total occupied/area texel count
57
+ */
58
+ function build_packs(charts, density, padding, rotate_charts, need_mask) {
59
+ const packs = [];
60
+ let mask_total = 0;
61
+
62
+ for (const chart of charts) {
63
+ const scale = Math.sqrt(chart.surface_area / Math.max(chart.parametric_area, 1e-12)) * density;
64
+
65
+ let min_u = Infinity, min_v = Infinity, max_u = -Infinity, max_v = -Infinity;
66
+ for (let i = 0; i < chart.vertex_count; i++) {
67
+ const u = chart.uv[i * 2];
68
+ const v = chart.uv[i * 2 + 1];
69
+ if (u < min_u) min_u = u;
70
+ if (u > max_u) max_u = u;
71
+ if (v < min_v) min_v = v;
72
+ if (v > max_v) max_v = v;
73
+ }
74
+
75
+ const ext_w = Math.max(1, Math.ceil((max_u - min_u) * scale) + 1);
76
+ const ext_h = Math.max(1, Math.ceil((max_v - min_v) * scale) + 1);
77
+ const w = ext_w + 2 * padding;
78
+ const h = ext_h + 2 * padding;
79
+
80
+ let mask = null;
81
+ let mask_t = null;
82
+ if (need_mask) {
83
+ mask = new BitImage2(w, h);
84
+ for (let t = 0; t < chart.triangle_count; t++) {
85
+ const i0 = chart.tri_local[t * 3];
86
+ const i1 = chart.tri_local[t * 3 + 1];
87
+ const i2 = chart.tri_local[t * 3 + 2];
88
+ tri2_rasterize_conservative(
89
+ mask,
90
+ (chart.uv[i0 * 2] - min_u) * scale + padding, (chart.uv[i0 * 2 + 1] - min_v) * scale + padding,
91
+ (chart.uv[i1 * 2] - min_u) * scale + padding, (chart.uv[i1 * 2 + 1] - min_v) * scale + padding,
92
+ (chart.uv[i2 * 2] - min_u) * scale + padding, (chart.uv[i2 * 2 + 1] - min_v) * scale + padding
93
+ );
94
+ }
95
+ if (padding > 0) {
96
+ mask.dilate(padding);
97
+ }
98
+ mask_t = rotate_charts ? mask.transposed() : null;
99
+ mask_total += mask.cardinality();
100
+ } else {
101
+ mask_total += w * h;
102
+ }
103
+
104
+ const placement = new ChartPlacement();
105
+ placement.chart = chart;
106
+ placement.scale = scale;
107
+ placement.min_u = min_u;
108
+ placement.min_v = min_v;
109
+ placement.w = w;
110
+ placement.h = h;
111
+ placement.mask = mask;
112
+ placement.mask_t = mask_t;
113
+ packs.push(placement);
114
+ }
115
+
116
+ return { packs, mask_total };
117
+ }
118
+
119
+ /**
120
+ * Largest-perimeter-first order (first-fit-decreasing).
121
+ *
122
+ * @param {ChartPlacement[]} packs placements to order
123
+ * @returns {number[]} pack indices sorted by descending (w + h)
124
+ */
125
+ function pack_order(packs) {
126
+ const order = packs.map((_, i) => i);
127
+ order.sort((a, b) => (packs[b].w + packs[b].h) - (packs[a].w + packs[a].h));
128
+ return order;
129
+ }
130
+
131
+ /**
132
+ * Pick the bottom-left-most of two candidate placements (rot 0 / rot 1).
133
+ *
134
+ * @param {{x:number, y:number}|null} fit0 unrotated candidate position, or null
135
+ * @param {{x:number, y:number}|null} fit1 90-degree-rotated candidate position, or null
136
+ * @returns {{rot:number, pos:{x:number, y:number}}|null} chosen placement, or null if neither fits
137
+ */
138
+ function choose_placement(fit0, fit1) {
139
+ if (fit0 === null && fit1 === null) {
140
+ return null;
141
+ }
142
+ if (fit1 === null) {
143
+ return { rot: 0, pos: fit0 };
144
+ }
145
+ if (fit0 === null) {
146
+ return { rot: 1, pos: fit1 };
147
+ }
148
+ if (fit1.y < fit0.y || (fit1.y === fit0.y && fit1.x < fit0.x)) {
149
+ return { rot: 1, pos: fit1 };
150
+ }
151
+ return { rot: 0, pos: fit0 };
152
+ }
153
+
154
+ // ─── Skyline rectangle packer (default, scalable) ────────────────────────────
155
+ /**
156
+ * Try to place one placement into the skyline page, choosing the bottom-left-most
157
+ * of the unrotated and (if allowed) 90-degree-rotated fit. On success, writes
158
+ * page/rot/x/y onto the placement and raises the skyline. Non-allocating —
159
+ * reads {@link Skyline.found_x}/{@link Skyline.found_y} after each `find`.
160
+ *
161
+ * @param {Skyline} sky page skyline
162
+ * @param {ChartPlacement} pack placement to position (mutated on success)
163
+ * @param {number} page_index
164
+ * @param {boolean} rotate_charts allow 90-degree placement
165
+ * @returns {boolean} true if placed
166
+ */
167
+ function skyline_try_place(sky, pack, page_index, rotate_charts) {
168
+ const ok0 = sky.find(pack.w, pack.h);
169
+ const x0 = sky.found_x;
170
+ const y0 = sky.found_y;
171
+
172
+ let ok1 = false;
173
+ let x1 = 0;
174
+ let y1 = 0;
175
+ if (rotate_charts) {
176
+ ok1 = sky.find(pack.h, pack.w);
177
+ x1 = sky.found_x;
178
+ y1 = sky.found_y;
179
+ }
180
+
181
+ let rot = -1;
182
+ let px = 0;
183
+ let py = 0;
184
+ if (ok0) {
185
+ rot = 0;
186
+ px = x0;
187
+ py = y0;
188
+ }
189
+ if (ok1 && (rot === -1 || y1 < py || (y1 === py && x1 < px))) {
190
+ rot = 1;
191
+ px = x1;
192
+ py = y1;
193
+ }
194
+ if (rot === -1) {
195
+ return false;
196
+ }
197
+
198
+ pack.page = page_index;
199
+ pack.rot = rot;
200
+ pack.x = px;
201
+ pack.y = py;
202
+ if (rot === 0) {
203
+ sky.place(px, py, pack.w, pack.h);
204
+ } else {
205
+ sky.place(px, py, pack.h, pack.w);
206
+ }
207
+ return true;
208
+ }
209
+
210
+ /**
211
+ * Place all packs on a single skyline page that grows by powers of two until
212
+ * every pack fits (xatlas growing-page mode, skyline variant).
213
+ *
214
+ * @param {ChartPlacement[]} packs
215
+ * @param {number} start_resolution initial square page size
216
+ * @param {boolean} rotate_charts allow 90-degree placement
217
+ * @returns {Skyline[]} single-element page list
218
+ */
219
+ function place_growing_skyline(packs, start_resolution, rotate_charts) {
220
+ const sky = new Skyline(start_resolution, start_resolution);
221
+ for (const idx of pack_order(packs)) {
222
+ const p = packs[idx];
223
+ while (!skyline_try_place(sky, p, 0, rotate_charts)) {
224
+ const need = Math.max(p.w, p.h);
225
+ const nw = ceilPowerOfTwo(Math.max(sky.width + 1, need));
226
+ const nh = ceilPowerOfTwo(Math.max(sky.height + 1, need));
227
+ if (nw > MAX_ATLAS_SIZE || nh > MAX_ATLAS_SIZE) {
228
+ throw new Error("atlas_pack_charts: atlas grew past the size limit");
229
+ }
230
+ sky.grow(nw, nh);
231
+ }
232
+ }
233
+ return [sky];
234
+ }
235
+
236
+ /**
237
+ * Place all packs into up to `max_pages` fixed-size skyline pages at the current
238
+ * density, spilling onto a fresh page when one fills.
239
+ *
240
+ * @param {ChartPlacement[]} packs
241
+ * @param {number} page_size fixed square page size
242
+ * @param {number} max_pages page cap
243
+ * @param {boolean} rotate_charts allow 90-degree placement
244
+ * @returns {Skyline[]|null} page list, or null if the packs do not fit
245
+ */
246
+ function place_fixed_skyline(packs, page_size, max_pages, rotate_charts) {
247
+ const pages = [new Skyline(page_size, page_size)];
248
+ for (const idx of pack_order(packs)) {
249
+ const p = packs[idx];
250
+
251
+ let placed = false;
252
+ for (let pi = 0; pi < pages.length; pi++) {
253
+ if (skyline_try_place(pages[pi], p, pi, rotate_charts)) {
254
+ placed = true;
255
+ break;
256
+ }
257
+ }
258
+ if (placed) {
259
+ continue;
260
+ }
261
+ if (pages.length >= max_pages) {
262
+ return null;
263
+ }
264
+ const fresh = new Skyline(page_size, page_size);
265
+ if (!skyline_try_place(fresh, p, pages.length, rotate_charts)) {
266
+ return null;
267
+ }
268
+ pages.push(fresh);
269
+ }
270
+ return pages;
271
+ }
272
+
273
+ // ─── Bitmap canBlit packer (exact, small atlases / bruteForce) ───────────────
274
+ /**
275
+ * Lowest-then-leftmost (row-major scan) position where `mask` can be blitted
276
+ * into `page` without overlapping a set bit, or null if it does not fit.
277
+ *
278
+ * @param {BitImage2} page atlas occupancy
279
+ * @param {BitImage2} mask chart coverage mask
280
+ * @returns {{x:number, y:number}|null}
281
+ */
282
+ function find_first_fit(page, mask) {
283
+ const max_y = page.height - mask.height;
284
+ const max_x = page.width - mask.width;
285
+ for (let y = 0; y <= max_y; y++) {
286
+ for (let x = 0; x <= max_x; x++) {
287
+ if (page.canBlit(mask, x, y)) {
288
+ return { x, y };
289
+ }
290
+ }
291
+ }
292
+ return null;
293
+ }
294
+
295
+ /**
296
+ * Try to place one pack into a bitmap page, choosing the bottom-left-most of the
297
+ * unrotated and (if a transpose exists) 90-degree-rotated fit. On success, writes
298
+ * page/rot/x/y onto the pack and ORs its mask into the page.
299
+ *
300
+ * @param {BitImage2} page atlas occupancy
301
+ * @param {ChartPlacement} pack placement to position (mutated on success)
302
+ * @param {number} page_index
303
+ * @returns {boolean} true if placed
304
+ */
305
+ function bitmap_try_place(page, pack, page_index) {
306
+ const fit0 = find_first_fit(page, pack.mask);
307
+ const fit1 = pack.mask_t !== null ? find_first_fit(page, pack.mask_t) : null;
308
+ const choice = choose_placement(fit0, fit1);
309
+ if (choice === null) {
310
+ return false;
311
+ }
312
+ pack.page = page_index;
313
+ pack.rot = choice.rot;
314
+ pack.x = choice.pos.x;
315
+ pack.y = choice.pos.y;
316
+ page.or_blit(choice.rot === 1 ? pack.mask_t : pack.mask, pack.x, pack.y);
317
+ return true;
318
+ }
319
+
320
+ /**
321
+ * Place all packs on a single bitmap page that grows by powers of two until
322
+ * every pack fits (xatlas growing-page mode, exact bitmap variant).
323
+ *
324
+ * @param {ChartPlacement[]} packs
325
+ * @param {number} start_resolution initial square page size
326
+ * @returns {BitImage2[]} single-element page list
327
+ */
328
+ function place_growing_bitmap(packs, start_resolution) {
329
+ const page = new BitImage2(start_resolution, start_resolution);
330
+ for (const idx of pack_order(packs)) {
331
+ const p = packs[idx];
332
+ while (!bitmap_try_place(page, p, 0)) {
333
+ const need_w = Math.max(p.mask.width, p.mask_t !== null ? p.mask_t.width : 0);
334
+ const need_h = Math.max(p.mask.height, p.mask_t !== null ? p.mask_t.height : 0);
335
+ const nw = ceilPowerOfTwo(Math.max(page.width + 1, need_w));
336
+ const nh = ceilPowerOfTwo(Math.max(page.height + 1, need_h));
337
+ if (nw > MAX_ATLAS_SIZE || nh > MAX_ATLAS_SIZE) {
338
+ throw new Error("atlas_pack_charts: atlas grew past the size limit");
339
+ }
340
+ page.grow_preserving(nw, nh);
341
+ }
342
+ }
343
+ return [page];
344
+ }
345
+
346
+ /**
347
+ * Place all packs into up to `max_pages` fixed-size bitmap pages at the current
348
+ * density, spilling onto a fresh page when one fills.
349
+ *
350
+ * @param {ChartPlacement[]} packs
351
+ * @param {number} page_size fixed square page size
352
+ * @param {number} max_pages page cap
353
+ * @returns {BitImage2[]|null} page list, or null if the packs do not fit
354
+ */
355
+ function place_fixed_bitmap(packs, page_size, max_pages) {
356
+ const pages = [new BitImage2(page_size, page_size)];
357
+ for (const idx of pack_order(packs)) {
358
+ const p = packs[idx];
359
+ let placed = false;
360
+ for (let pi = 0; pi < pages.length; pi++) {
361
+ if (bitmap_try_place(pages[pi], p, pi)) {
362
+ placed = true;
363
+ break;
364
+ }
365
+ }
366
+ if (placed) {
367
+ continue;
368
+ }
369
+ if (pages.length >= max_pages) {
370
+ return null;
371
+ }
372
+ const fresh = new BitImage2(page_size, page_size);
373
+ if (!bitmap_try_place(fresh, p, pages.length)) {
374
+ return null;
375
+ }
376
+ pages.push(fresh);
377
+ }
378
+ return pages;
379
+ }
380
+
381
+ /**
382
+ * Pack parameterized charts into one or more atlas pages (xatlas `PackCharts`),
383
+ * mutating each chart in place: its `uv` becomes final atlas-space [0,1]
384
+ * coordinates and `atlas_page`/`atlas_x`/`atlas_y`/`atlas_rotated` record its
385
+ * placement. Charts are pre-rotated to their min-area box and may be placed
386
+ * 90-degrees rotated for density.
387
+ *
388
+ * This works purely at the {@link Chart} level — no mesh — so charts from several
389
+ * different meshes can be packed into a single shared atlas. To write the packed
390
+ * UVs back onto a mesh, call {@link atlas_write_chart_uvs} (once per source mesh).
391
+ *
392
+ * Placement strategy is chosen by atlas size: small atlases use the exact
393
+ * bitmap shifted-OR `canBlit` packer (tight, non-rectangular interlocking);
394
+ * large atlases use the fast O(n) skyline rectangle packer (so packing scales).
395
+ * `bruteForce: true` forces the bitmap packer regardless of size.
396
+ *
397
+ * Page modes:
398
+ * - `maxResolution = 0` (default): a single page that grows by powers of two.
399
+ * - `maxResolution > 0`, `maxPages = 1` (default): one fixed-size page with
400
+ * **elastic density** — density shrinks until the charts fit.
401
+ * - `maxResolution > 0`, `maxPages > 1`: spill into up to `maxPages` fixed-size
402
+ * pages at fixed density (`maxPages = Infinity` = pure fixed-density spill).
403
+ *
404
+ * @param {Chart[]} charts from {@link atlas_compute_charts} (mutated: `uv` + placement)
405
+ * @param {object} [options]
406
+ * @param {number} [options.padding] gutter texels (default 1)
407
+ * @param {number} [options.resolution] start/target size for the growing page; 0 = auto
408
+ * @param {number} [options.texelsPerUnit] texel density; 0 = auto
409
+ * @param {number} [options.maxResolution] fixed page size; 0 = unbounded growing page (default 0)
410
+ * @param {number} [options.maxPages] page cap; density is elastic to honour it (default 1)
411
+ * @param {boolean} [options.bruteForce] force the exact bitmap packer (default false)
412
+ * @param {boolean} [options.rotateChartsToAxis] align charts to their OBB (default true)
413
+ * @param {boolean} [options.rotateCharts] allow 90-degree placement (default true)
414
+ * @returns {{pages: Array<{width:number, height:number, occupied_texels:number, utilization:number}>,
415
+ * page_count:number, atlas_width:number, atlas_height:number, utilization:number,
416
+ * occupied_texels:number, mask_texels_total:number, charts:Chart[]}}
417
+ */
418
+ export function atlas_pack_charts(charts, options = {}) {
419
+ const padding = options.padding ?? 1;
420
+ const rotate_to_axis = options.rotateChartsToAxis ?? true;
421
+ const rotate_charts = options.rotateCharts ?? true;
422
+ const max_resolution = options.maxResolution ?? 0;
423
+ const max_pages = options.maxPages ?? 1;
424
+ const brute_force = options.bruteForce ?? false;
425
+
426
+ if (rotate_to_axis) {
427
+ for (const chart of charts) {
428
+ rotate_uv_to_min_area_box(chart);
429
+ }
430
+ }
431
+
432
+ let total_surface = 0;
433
+ for (const c of charts) {
434
+ total_surface += c.surface_area;
435
+ }
436
+ if (total_surface <= 0) {
437
+ total_surface = 1;
438
+ }
439
+
440
+ let packs;
441
+ let mask_total;
442
+ let pages;
443
+ let use_skyline;
444
+
445
+ if (max_resolution === 0) {
446
+ let resolution = options.resolution ?? 0;
447
+ let density = options.texelsPerUnit ?? 0;
448
+ if (resolution === 0 && density === 0) {
449
+ resolution = 1024;
450
+ }
451
+ if (density === 0) {
452
+ density = resolution * Math.sqrt(UTILIZATION_TARGET / total_surface);
453
+ }
454
+ if (resolution === 0) {
455
+ resolution = Math.max(1, Math.ceil(Math.sqrt(total_surface * density * density / UTILIZATION_TARGET)));
456
+ }
457
+
458
+ use_skyline = !brute_force && resolution * resolution > BRUTE_FORCE_AREA;
459
+ const built = build_packs(charts, density, padding, rotate_charts, !use_skyline);
460
+ packs = built.packs;
461
+ mask_total = built.mask_total;
462
+ pages = use_skyline ? place_growing_skyline(packs, resolution, rotate_charts) : place_growing_bitmap(packs, resolution);
463
+ } else {
464
+ use_skyline = !brute_force && max_resolution * max_resolution > BRUTE_FORCE_AREA;
465
+ const estimate_pages = Number.isFinite(max_pages) ? max_pages : 1;
466
+ let density = options.texelsPerUnit ?? 0;
467
+ if (density === 0) {
468
+ density = Math.sqrt(UTILIZATION_TARGET * estimate_pages * max_resolution * max_resolution / total_surface);
469
+ }
470
+
471
+ for (let attempt = 0; attempt < MAX_ELASTIC_ATTEMPTS; attempt++) {
472
+ const built = build_packs(charts, density, padding, rotate_charts, !use_skyline);
473
+ const placed = use_skyline
474
+ ? place_fixed_skyline(built.packs, max_resolution, max_pages, rotate_charts)
475
+ : place_fixed_bitmap(built.packs, max_resolution, max_pages);
476
+ if (placed !== null) {
477
+ packs = built.packs;
478
+ mask_total = built.mask_total;
479
+ pages = placed;
480
+ break;
481
+ }
482
+ density *= ELASTIC_SHRINK;
483
+ }
484
+ if (pages === undefined) {
485
+ throw new Error("atlas_pack_charts: could not fit charts within maxPages pages");
486
+ }
487
+ }
488
+
489
+ // Update each chart's UVs to final atlas-space coordinates (page-relative)
490
+ // and record its placement. Actualizing onto a mesh is a separate, explicit
491
+ // step (atlas_write_chart_uvs) the caller controls.
492
+ for (const p of packs) {
493
+ const page = pages[p.page];
494
+ const pw = page.width;
495
+ const ph = page.height;
496
+ const chart = p.chart;
497
+ const final_uv = new Float64Array(chart.vertex_count * 2);
498
+ for (let i = 0; i < chart.vertex_count; i++) {
499
+ const tu = (chart.uv[i * 2] - p.min_u) * p.scale;
500
+ const tv = (chart.uv[i * 2 + 1] - p.min_v) * p.scale;
501
+ if (p.rot === 1) {
502
+ final_uv[i * 2] = (p.x + padding + tv) / pw;
503
+ final_uv[i * 2 + 1] = (p.y + padding + tu) / ph;
504
+ } else {
505
+ final_uv[i * 2] = (p.x + padding + tu) / pw;
506
+ final_uv[i * 2 + 1] = (p.y + padding + tv) / ph;
507
+ }
508
+ }
509
+ chart.uv = final_uv;
510
+ chart.atlas_page = p.page;
511
+ chart.atlas_x = p.x;
512
+ chart.atlas_y = p.y;
513
+ chart.atlas_rotated = p.rot === 1;
514
+ }
515
+
516
+ // Per-page occupancy: bitmap = set texels; skyline = placed rectangle areas.
517
+ const occupied = new Array(pages.length).fill(0);
518
+ if (use_skyline) {
519
+ for (const p of packs) {
520
+ occupied[p.page] += p.w * p.h;
521
+ }
522
+ } else {
523
+ for (let i = 0; i < pages.length; i++) {
524
+ occupied[i] = pages[i].cardinality();
525
+ }
526
+ }
527
+
528
+ const page_infos = pages.map((pg, i) => ({
529
+ width: pg.width,
530
+ height: pg.height,
531
+ occupied_texels: occupied[i],
532
+ utilization: occupied[i] / (pg.width * pg.height),
533
+ }));
534
+ let total_occupied = 0;
535
+ let total_cells = 0;
536
+ for (const info of page_infos) {
537
+ total_occupied += info.occupied_texels;
538
+ total_cells += info.width * info.height;
539
+ }
540
+
541
+ return {
542
+ pages: page_infos,
543
+ page_count: pages.length,
544
+ atlas_width: page_infos[0].width,
545
+ atlas_height: page_infos[0].height,
546
+ utilization: total_occupied / total_cells,
547
+ occupied_texels: total_occupied,
548
+ mask_texels_total: mask_total,
549
+ charts,
550
+ };
551
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Validity of a chart's current UV layout (xatlas
3
+ * `isChartParameterizationValid`). A layout is bijective when every triangle
4
+ * has the same winding (all positive or all flipped — a uniformly mirrored
5
+ * chart is fine) with no zero-area triangles, and the chart outline does not
6
+ * self-intersect.
7
+ *
8
+ * Boundary self-intersection is a brute-force O(b^2) test over the chart's
9
+ * boundary edges, skipping pairs that share an endpoint.
10
+ *
11
+ * @param {Chart} chart
12
+ * @returns {{positive_count:number, flipped_count:number, zero_area_count:number,
13
+ * mixed_winding:boolean, self_intersects:boolean}}
14
+ */
15
+ export function atlas_chart_param_validity(chart: Chart): {
16
+ positive_count: number;
17
+ flipped_count: number;
18
+ zero_area_count: number;
19
+ mixed_winding: boolean;
20
+ self_intersects: boolean;
21
+ };
22
+ //# sourceMappingURL=atlas_chart_param_validity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"atlas_chart_param_validity.d.ts","sourceRoot":"","sources":["../../../../../../../src/core/geom/3d/atlas/param/atlas_chart_param_validity.js"],"names":[],"mappings":"AAKA;;;;;;;;;;;;;GAaG;AACH;oBAH6B,MAAM;mBAAgB,MAAM;qBAAkB,MAAM;mBAC9D,OAAO;qBAAkB,OAAO;EAuDlD"}
@@ -0,0 +1,73 @@
1
+ import { segment2_intersects_segment2_2d } from "../../../2d/line/segment2_intersects_segment2_2d.js";
2
+ import { tri2_signed_area } from "../../../2d/triangle/tri2_signed_area.js";
3
+
4
+ const AREA_EPSILON = 1e-12;
5
+
6
+ /**
7
+ * Validity of a chart's current UV layout (xatlas
8
+ * `isChartParameterizationValid`). A layout is bijective when every triangle
9
+ * has the same winding (all positive or all flipped — a uniformly mirrored
10
+ * chart is fine) with no zero-area triangles, and the chart outline does not
11
+ * self-intersect.
12
+ *
13
+ * Boundary self-intersection is a brute-force O(b^2) test over the chart's
14
+ * boundary edges, skipping pairs that share an endpoint.
15
+ *
16
+ * @param {Chart} chart
17
+ * @returns {{positive_count:number, flipped_count:number, zero_area_count:number,
18
+ * mixed_winding:boolean, self_intersects:boolean}}
19
+ */
20
+ export function atlas_chart_param_validity(chart) {
21
+ const uv = chart.uv;
22
+ const tri = chart.tri_local;
23
+ const triangle_count = chart.triangle_count;
24
+
25
+ let positive_count = 0;
26
+ let flipped_count = 0;
27
+ let zero_area_count = 0;
28
+
29
+ for (let t = 0; t < triangle_count; t++) {
30
+ const i0 = tri[t * 3];
31
+ const i1 = tri[t * 3 + 1];
32
+ const i2 = tri[t * 3 + 2];
33
+ const area = tri2_signed_area(
34
+ uv[i0 * 2], uv[i0 * 2 + 1],
35
+ uv[i1 * 2], uv[i1 * 2 + 1],
36
+ uv[i2 * 2], uv[i2 * 2 + 1]
37
+ );
38
+ if (area > AREA_EPSILON) positive_count++;
39
+ else if (area < -AREA_EPSILON) flipped_count++;
40
+ else zero_area_count++;
41
+ }
42
+
43
+ const mixed_winding = positive_count > 0 && flipped_count > 0;
44
+
45
+ const be = chart.boundary_edges;
46
+ const edge_count = be.length / 2;
47
+ let self_intersects = false;
48
+
49
+ outer:
50
+ for (let i = 0; i < edge_count; i++) {
51
+ const a0 = be[i * 2];
52
+ const a1 = be[i * 2 + 1];
53
+ const a0x = uv[a0 * 2], a0y = uv[a0 * 2 + 1];
54
+ const a1x = uv[a1 * 2], a1y = uv[a1 * 2 + 1];
55
+
56
+ for (let j = i + 1; j < edge_count; j++) {
57
+ const b0 = be[j * 2];
58
+ const b1 = be[j * 2 + 1];
59
+ // adjacent edges share an endpoint — a shared endpoint is not a crossing
60
+ if (a0 === b0 || a0 === b1 || a1 === b0 || a1 === b1) {
61
+ continue;
62
+ }
63
+ const b0x = uv[b0 * 2], b0y = uv[b0 * 2 + 1];
64
+ const b1x = uv[b1 * 2], b1y = uv[b1 * 2 + 1];
65
+ if (segment2_intersects_segment2_2d(a0x, a0y, a1x, a1y, b0x, b0y, b1x, b1y)) {
66
+ self_intersects = true;
67
+ break outer;
68
+ }
69
+ }
70
+ }
71
+
72
+ return { positive_count, flipped_count, zero_area_count, mixed_winding, self_intersects };
73
+ }
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Intrinsic boundary-first flattening (a tractable BFF, Sawhney & Crane 2018).
3
+ *
4
+ * Unlike a Tutte/convex embedding (which forces a circle and so distorts
5
+ * thin/curled charts catastrophically — the aspect cannot be guessed without
6
+ * already flattening), this builds the flattened boundary from the chart's OWN
7
+ * geometry: keep each boundary edge's 3D length and turn at each boundary vertex
8
+ * by the optimal Dirichlet-to-Neumann curvature — fix the boundary scale to 0,
9
+ * solve the interior Poisson `L_II u = Ω_I` (flatten the interior), then
10
+ * `k̃_b = (π − θ_b) − (L u)_b`. By the cot-Laplacian's zero row sums ∑ k̃ = 2π so
11
+ * the boundary closes, with the curvature distributed by the mesh's harmonic
12
+ * structure (not uniformly). The interior is then a CONFORMAL extension of that
13
+ * boundary (least-squares Cauchy-Riemann, every boundary vertex pinned) — the u,v
14
+ * coupling is far less flip-prone than a harmonic interior on a non-convex
15
+ * boundary. The result is a natural, low-distortion, length-preserving boundary —
16
+ * not a forced convex shape.
17
+ *
18
+ * Measured outcome (decimated terrain): a Pareto-safe rescue but SMALL — the DtN
19
+ * boundary fixed the conformal-interior flips (25 → 2), yet only ~2 of 31
20
+ * self-intersecting charts are rescued because the flattened BOUNDARY itself then
21
+ * self-intersects on ~27/31. That is fundamental, not an implementation gap (true
22
+ * unclamped cotangents made it worse): these high-curvature decimated charts can't
23
+ * be developed to a SIMPLE boundary while preserving edge lengths, so cutting
24
+ * (piecewise) is the correct injective answer for them. Injectivity is NOT
25
+ * guaranteed; the caller validates and falls back to piecewise otherwise. Solves
26
+ * in place on `chart.uv`. See BFF_PLAN.md / REVIEW_02.md §5.0.
27
+ *
28
+ * @param {Chart} chart with `boundary_edges`, `tri_local`, `positions`, `uv`
29
+ * @param {{tolerance?:number, maxIterations?:number}} [options]
30
+ * @returns {boolean} true if a flattening was produced; false if the chart is not
31
+ * a single-boundary-loop disk or the interior system is degenerate
32
+ */
33
+ export function atlas_chart_parameterize_bff(chart: Chart, options?: {
34
+ tolerance?: number;
35
+ maxIterations?: number;
36
+ }): boolean;
37
+ //# sourceMappingURL=atlas_chart_parameterize_bff.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"atlas_chart_parameterize_bff.d.ts","sourceRoot":"","sources":["../../../../../../../src/core/geom/3d/atlas/param/atlas_chart_parameterize_bff.js"],"names":[],"mappings":"AAoDA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH;gBAJuB,MAAM;oBAAiB,MAAM;IACvC,OAAO,CA4LnB"}