@woosh/meep-engine 2.168.1 → 2.169.1
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.
- package/README.md +2 -2
- package/build/bundle-worker-image-decoder.js +1 -1
- package/editor/process/symbolic/make3DSymbolicDisplay.js +1 -1
- package/editor/view/ecs/EntityList.js +1 -1
- package/editor/view/ecs/components/particles/ParticleLayerController.js +1 -1
- package/editor/view/makeEntityDecorators.js +1 -1
- package/package.json +1 -1
- package/src/REVIEW_2026_08_06/adv-core.md +511 -0
- package/src/REVIEW_2026_08_06/adv-gfx.md +480 -0
- package/src/REVIEW_2026_08_06/adv-view.md +446 -0
- package/src/REVIEW_2026_08_06/api-core.md +296 -0
- package/src/REVIEW_2026_08_06/api-engine.md +195 -0
- package/src/REVIEW_2026_08_06/bvh-phys-sound-net.md +507 -0
- package/src/REVIEW_2026_08_06/core-binary.md +348 -0
- package/src/REVIEW_2026_08_06/core-collection.md +376 -0
- package/src/REVIEW_2026_08_06/core-math.md +221 -0
- package/src/REVIEW_2026_08_06/core-process.md +266 -0
- package/src/REVIEW_2026_08_06/core-science.md +223 -0
- package/src/REVIEW_2026_08_06/engine-ai-nav.md +279 -0
- package/src/REVIEW_2026_08_06/engine-animation.md +167 -0
- package/src/REVIEW_2026_08_06/engine-asset.md +166 -0
- package/src/REVIEW_2026_08_06/engine-ecs.md +920 -0
- package/src/REVIEW_2026_08_06/engine-input.md +197 -0
- package/src/REVIEW_2026_08_06/engine-misc.md +248 -0
- package/src/REVIEW_2026_08_06/engine-terrain.md +52 -0
- package/src/REVIEW_2026_08_06/generation.md +176 -0
- package/src/REVIEW_2026_08_06/geom-2d.md +434 -0
- package/src/REVIEW_2026_08_06/geom-3d.md +117 -0
- package/src/REVIEW_2026_08_06/gfx-gi.md +143 -0
- package/src/REVIEW_2026_08_06/gfx-render.md +740 -0
- package/src/REVIEW_2026_08_06/gfx-texture.md +690 -0
- package/src/REVIEW_2026_08_06/prior-criticals.md +49 -0
- package/src/REVIEW_2026_08_06/prior-highs.md +138 -0
- package/src/REVIEW_2026_08_06/test-gaps.md +364 -0
- package/src/REVIEW_2026_08_06/view.md +667 -0
- package/src/REVIEW_2026_08_06.md +610 -0
- package/src/core/IdPool.d.ts +2 -2
- package/src/core/IdPool.d.ts.map +1 -1
- package/src/core/IdPool.js +1 -1
- package/src/core/binary/32BitEncoder.d.ts +2 -3
- package/src/core/binary/32BitEncoder.d.ts.map +1 -1
- package/src/core/binary/32BitEncoder.js +1 -1
- package/src/core/binary/BinaryBuffer.d.ts.map +1 -1
- package/src/core/binary/BinaryBuffer.js +12 -1
- package/src/core/binary/BitImage2.d.ts +5 -1
- package/src/core/binary/BitImage2.d.ts.map +1 -1
- package/src/core/binary/BitImage2.js +5 -1
- package/src/core/bvh2/bvh3/BVH.d.ts +2 -2
- package/src/core/bvh2/bvh3/BVH.d.ts.map +1 -1
- package/src/core/bvh2/bvh3/BVH.js +1 -1
- package/src/core/bvh2/bvh3/query/bvh_query_user_data_nearest_to_point_filtered.d.ts +2 -2
- package/src/core/bvh2/bvh3/query/bvh_query_user_data_nearest_to_point_filtered.d.ts.map +1 -1
- package/src/core/bvh2/bvh3/query/bvh_query_user_data_nearest_to_point_filtered.js +86 -86
- package/src/core/bvh2/traversal/aabb3_detailed_volume_intersection_callback_based.d.ts +2 -2
- package/src/core/bvh2/traversal/aabb3_detailed_volume_intersection_callback_based.d.ts.map +1 -1
- package/src/core/bvh2/traversal/aabb3_detailed_volume_intersection_callback_based.js +1 -1
- package/src/core/bvh8/build/NodeProxy.d.ts.map +1 -1
- package/src/core/bvh8/build/NodeProxy.js +314 -308
- package/src/core/cache/Cache.d.ts +4 -4
- package/src/core/cache/Cache.d.ts.map +1 -1
- package/src/core/cache/Cache.js +3 -3
- package/src/core/cache/LoadingCache.d.ts +1 -1
- package/src/core/cache/LoadingCache.js +2 -2
- package/src/core/cache/wtinylfu/CacheWTinylfu.d.ts +2 -2
- package/src/core/cache/wtinylfu/CacheWTinylfu.js +2 -2
- package/src/core/collection/array/array_index_by_equality.d.ts +2 -2
- package/src/core/collection/array/array_index_by_equality.d.ts.map +1 -1
- package/src/core/collection/array/array_index_by_equality.js +1 -1
- package/src/core/collection/array/array_pick_best_element.d.ts +2 -2
- package/src/core/collection/array/array_pick_best_element.d.ts.map +1 -1
- package/src/core/collection/array/array_pick_best_element.js +1 -1
- package/src/core/collection/array/array_pick_best_elements.d.ts +2 -2
- package/src/core/collection/array/array_pick_best_elements.d.ts.map +1 -1
- package/src/core/collection/array/array_pick_best_elements.js +1 -1
- package/src/core/collection/array/array_set_diff.d.ts +2 -3
- package/src/core/collection/array/array_set_diff.d.ts.map +1 -1
- package/src/core/collection/array/array_set_diff.js +1 -1
- package/src/core/collection/array/array_set_diff_sorting.d.ts +2 -2
- package/src/core/collection/array/array_set_diff_sorting.d.ts.map +1 -1
- package/src/core/collection/array/array_set_diff_sorting.js +1 -1
- package/src/core/collection/array/iterator/ArrayIteratorRandom.d.ts +2 -1
- package/src/core/collection/array/iterator/ArrayIteratorRandom.d.ts.map +1 -1
- package/src/core/collection/heap/BinaryHeap.d.ts +2 -2
- package/src/core/collection/heap/BinaryHeap.d.ts.map +1 -1
- package/src/core/collection/heap/BinaryHeap.js +1 -1
- package/src/core/collection/list/List.d.ts +32 -17
- package/src/core/collection/list/List.d.ts.map +1 -1
- package/src/core/collection/list/List.js +43 -9
- package/src/core/collection/list/SortedListProjection.d.ts.map +1 -1
- package/src/core/collection/list/SortedListProjection.js +4 -1
- package/src/core/collection/map/BiMap.d.ts +25 -2
- package/src/core/collection/map/BiMap.d.ts.map +1 -1
- package/src/core/collection/map/BiMap.js +57 -3
- package/src/core/collection/map/HashMap.d.ts +2 -2
- package/src/core/collection/map/HashMap.d.ts.map +1 -1
- package/src/core/collection/map/HashMap.js +1 -1
- package/src/core/collection/set/ArraySet.d.ts +2 -2
- package/src/core/collection/set/ArraySet.d.ts.map +1 -1
- package/src/core/collection/set/ArraySet.js +1 -1
- package/src/core/collection/table/RowFirstTable.d.ts +2 -2
- package/src/core/collection/table/RowFirstTable.d.ts.map +1 -1
- package/src/core/collection/table/RowFirstTable.js +1 -1
- package/src/core/collection/table/RowFirstTableSpec.js +4 -4
- package/src/core/debug/matchers/IsAnything.d.ts.map +1 -1
- package/src/core/debug/matchers/IsAnything.js +5 -1
- package/src/core/events/signal/Signal.d.ts.map +1 -1
- package/src/core/events/signal/Signal.js +3 -1
- package/src/core/fsm/simple/SimpleStateMachineDescription.d.ts +2 -2
- package/src/core/fsm/simple/SimpleStateMachineDescription.d.ts.map +1 -1
- package/src/core/fsm/simple/SimpleStateMachineDescription.js +1 -1
- package/src/core/geom/2d/bvh/BVH2D.d.ts +2 -2
- package/src/core/geom/2d/bvh/BVH2D.d.ts.map +1 -1
- package/src/core/geom/2d/bvh/BVH2D.js +1 -1
- package/src/core/geom/2d/polygon/TRIANGULATION_DESIGN.md +433 -0
- package/src/core/geom/2d/polygon/polygon2_is_counter_clockwise.d.ts +31 -0
- package/src/core/geom/2d/polygon/polygon2_is_counter_clockwise.d.ts.map +1 -0
- package/src/core/geom/2d/polygon/polygon2_is_counter_clockwise.js +84 -0
- package/src/core/geom/2d/polygon/polygon2_signed_area.d.ts +24 -2
- package/src/core/geom/2d/polygon/polygon2_signed_area.d.ts.map +1 -1
- package/src/core/geom/2d/polygon/polygon2_signed_area.js +52 -34
- package/src/core/geom/2d/polygon/polygon2_triangulate.corpus.d.ts +28 -0
- package/src/core/geom/2d/polygon/polygon2_triangulate.corpus.d.ts.map +1 -0
- package/src/core/geom/2d/polygon/polygon2_triangulate.corpus.js +86 -0
- package/src/core/geom/2d/polygon/polygon2_triangulate.d.ts +40 -0
- package/src/core/geom/2d/polygon/polygon2_triangulate.d.ts.map +1 -0
- package/src/core/geom/2d/polygon/polygon2_triangulate.js +1280 -0
- package/src/core/geom/2d/polygon/polygon2_triangulation_deviation.d.ts +28 -0
- package/src/core/geom/2d/polygon/polygon2_triangulation_deviation.d.ts.map +1 -0
- package/src/core/geom/2d/polygon/polygon2_triangulation_deviation.js +77 -0
- package/src/core/geom/2d/quad-tree/qt_match_data_by_circle.d.ts +2 -2
- package/src/core/geom/2d/quad-tree/qt_match_data_by_circle.d.ts.map +1 -1
- package/src/core/geom/2d/quad-tree/qt_match_data_by_circle.js +1 -1
- package/src/core/geom/2d/quad-tree/qt_query_data_raycast.d.ts +2 -2
- package/src/core/geom/2d/quad-tree/qt_query_data_raycast.d.ts.map +1 -1
- package/src/core/geom/2d/quad-tree/qt_query_data_raycast.js +1 -1
- package/src/core/geom/2d/triangle/tri2_rasterize_conservative.d.ts +6 -3
- package/src/core/geom/2d/triangle/tri2_rasterize_conservative.d.ts.map +1 -1
- package/src/core/geom/2d/triangle/tri2_rasterize_conservative.js +26 -13
- package/src/core/geom/2d/triangle/tri2_signed_area.d.ts +17 -1
- package/src/core/geom/2d/triangle/tri2_signed_area.d.ts.map +1 -1
- package/src/core/geom/2d/triangle/tri2_signed_area.js +35 -23
- package/src/core/geom/2d/v2_morton_encode.d.ts +20 -0
- package/src/core/geom/2d/v2_morton_encode.d.ts.map +1 -0
- package/src/core/geom/2d/v2_morton_encode.js +23 -0
- package/src/core/geom/3d/aabb/AABB3.d.ts +2 -2
- package/src/core/geom/3d/aabb/AABB3.d.ts.map +1 -1
- package/src/core/geom/3d/aabb/AABB3.js +1 -1
- package/src/core/geom/3d/atlas/segment/atlas_merge_charts.d.ts.map +1 -1
- package/src/core/geom/3d/atlas/segment/atlas_merge_charts.js +4 -1
- package/src/core/geom/3d/polygon/polygon3_compute_normal.d.ts +27 -0
- package/src/core/geom/3d/polygon/polygon3_compute_normal.d.ts.map +1 -0
- package/src/core/geom/3d/polygon/polygon3_compute_normal.js +60 -0
- package/src/core/geom/3d/polygon/polygon3_triangulate.d.ts +34 -0
- package/src/core/geom/3d/polygon/polygon3_triangulate.d.ts.map +1 -0
- package/src/core/geom/3d/polygon/polygon3_triangulate.js +94 -0
- package/src/core/geom/3d/quaternion/quat3_multiply.d.ts +8 -2
- package/src/core/geom/3d/quaternion/quat3_multiply.d.ts.map +1 -1
- package/src/core/geom/3d/quaternion/quat3_multiply.js +8 -2
- package/src/core/geom/3d/shape/ConvexHullShape3D.d.ts.map +1 -1
- package/src/core/geom/3d/shape/ConvexHullShape3D.js +32 -2
- package/src/core/geom/3d/tetrahedra/TetrahedralMesh.d.ts +2 -2
- package/src/core/geom/3d/tetrahedra/TetrahedralMesh.d.ts.map +1 -1
- package/src/core/geom/3d/tetrahedra/TetrahedralMesh.js +1 -1
- package/src/core/geom/3d/tetrahedra/delaunay/debug/push_boundary_with_validation.d.ts +2 -5
- package/src/core/geom/3d/tetrahedra/delaunay/debug/push_boundary_with_validation.d.ts.map +1 -1
- package/src/core/geom/3d/tetrahedra/delaunay/debug/push_boundary_with_validation.js +1 -1
- package/src/core/geom/3d/tetrahedra/delaunay/debug/validate_cavity_boundary.d.ts +2 -2
- package/src/core/geom/3d/tetrahedra/delaunay/debug/validate_cavity_boundary.d.ts.map +1 -1
- package/src/core/geom/3d/tetrahedra/delaunay/debug/validate_cavity_boundary.js +1 -1
- package/src/core/geom/3d/tetrahedra/validate_tetrahedral_mesh.d.ts +2 -3
- package/src/core/geom/3d/tetrahedra/validate_tetrahedral_mesh.d.ts.map +1 -1
- package/src/core/geom/3d/tetrahedra/validate_tetrahedral_mesh.js +69 -69
- package/src/core/geom/3d/tetrahedra/validate_tetrahedron_neighbourhood.d.ts +2 -3
- package/src/core/geom/3d/tetrahedra/validate_tetrahedron_neighbourhood.d.ts.map +1 -1
- package/src/core/geom/3d/tetrahedra/validate_tetrahedron_neighbourhood.js +50 -50
- package/src/core/geom/3d/topology/struct/TopoEdge.d.ts +2 -2
- package/src/core/geom/3d/topology/struct/TopoEdge.d.ts.map +1 -1
- package/src/core/geom/3d/topology/struct/TopoEdge.js +1 -1
- package/src/core/geom/3d/topology/struct/TopoMesh.d.ts +2 -3
- package/src/core/geom/3d/topology/struct/TopoMesh.d.ts.map +1 -1
- package/src/core/geom/3d/topology/struct/TopoMesh.js +1 -1
- package/src/core/geom/3d/topology/struct/TopoTriangle.d.ts +2 -2
- package/src/core/geom/3d/topology/struct/TopoTriangle.d.ts.map +1 -1
- package/src/core/geom/3d/topology/struct/TopoTriangle.js +1 -1
- package/src/core/geom/3d/topology/struct/TopoVertex.d.ts +2 -2
- package/src/core/geom/3d/topology/struct/TopoVertex.d.ts.map +1 -1
- package/src/core/geom/3d/topology/struct/TopoVertex.js +1 -1
- package/src/core/geom/3d/topology/struct/binary/io/face/bt_face_triangulate.d.ts +23 -6
- package/src/core/geom/3d/topology/struct/binary/io/face/bt_face_triangulate.d.ts.map +1 -1
- package/src/core/geom/3d/topology/struct/binary/io/face/bt_face_triangulate.js +268 -124
- package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_assert_valid.d.ts +17 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_assert_valid.d.ts.map +1 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_assert_valid.js +44 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_count_edge_faces.d.ts +20 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_count_edge_faces.d.ts.map +1 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_count_edge_faces.js +40 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_is_closed.d.ts +19 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_is_closed.d.ts.map +1 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_is_closed.js +34 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_is_manifold.d.ts +22 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_is_manifold.d.ts.map +1 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_is_manifold.js +37 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_validate.d.ts +87 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_validate.d.ts.map +1 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_validate.js +599 -0
- package/src/core/geom/3d/topology/util/mesh_flood_fill.d.ts +1 -1
- package/src/core/geom/3d/topology/util/mesh_flood_fill.js +1 -1
- package/src/core/geom/3d/triangle/tri3_compute_signed_volume.d.ts +32 -0
- package/src/core/geom/3d/triangle/tri3_compute_signed_volume.d.ts.map +1 -1
- package/src/core/geom/3d/triangle/tri3_compute_signed_volume.js +66 -7
- package/src/core/geom/3d/triangle/tri3_mesh_compute_signed_volume.d.ts +22 -1
- package/src/core/geom/3d/triangle/tri3_mesh_compute_signed_volume.d.ts.map +1 -1
- package/src/core/geom/3d/triangle/tri3_mesh_compute_signed_volume.js +83 -38
- package/src/core/geom/3d/util/aabb3_emit_point_grid.d.ts +2 -2
- package/src/core/geom/3d/util/aabb3_emit_point_grid.d.ts.map +1 -1
- package/src/core/geom/3d/util/aabb3_emit_point_grid.js +1 -1
- package/src/core/geom/MANIFOLD_COMPARISON_2026_08_01.md +554 -0
- package/src/core/geom/Quaternion.d.ts +2 -2
- package/src/core/geom/Quaternion.d.ts.map +1 -1
- package/src/core/geom/Quaternion.js +1 -1
- package/src/core/geom/Vector1.d.ts +2 -2
- package/src/core/geom/Vector1.d.ts.map +1 -1
- package/src/core/geom/Vector1.js +1 -1
- package/src/core/geom/packing/max-rect/find_best_container.d.ts +2 -2
- package/src/core/geom/packing/max-rect/find_best_container.d.ts.map +1 -1
- package/src/core/geom/packing/max-rect/find_best_container.js +1 -1
- package/src/core/geom/vec3/v3_cotangent.d.ts +6 -2
- package/src/core/geom/vec3/v3_cotangent.d.ts.map +1 -1
- package/src/core/geom/vec3/v3_cotangent.js +6 -2
- package/src/core/geom/vec3/v3_cross.d.ts +6 -2
- package/src/core/geom/vec3/v3_cross.d.ts.map +1 -1
- package/src/core/geom/vec3/v3_cross.js +6 -2
- package/src/core/geom/vec3/v3_lerp.d.ts +1 -1
- package/src/core/geom/vec3/v3_lerp.js +1 -1
- package/src/core/geom/vec3/v3_multiply.d.ts +6 -2
- package/src/core/geom/vec3/v3_multiply.d.ts.map +1 -1
- package/src/core/geom/vec3/v3_multiply.js +6 -2
- package/src/core/geom/vec3/v3_subtract.d.ts +6 -2
- package/src/core/geom/vec3/v3_subtract.d.ts.map +1 -1
- package/src/core/geom/vec3/v3_subtract.js +6 -2
- package/src/core/json/abstractJSONSerializer.d.ts +2 -2
- package/src/core/json/abstractJSONSerializer.d.ts.map +1 -1
- package/src/core/json/abstractJSONSerializer.js +1 -1
- package/src/core/lang/reactive/AbstractCachingParser.d.ts +2 -3
- package/src/core/lang/reactive/AbstractCachingParser.d.ts.map +1 -1
- package/src/core/lang/reactive/AbstractCachingParser.js +1 -1
- package/src/core/localization/Localization.d.ts +2 -2
- package/src/core/localization/Localization.d.ts.map +1 -1
- package/src/core/localization/Localization.js +2 -2
- package/src/core/math/noise/create_simplex_noise_2d.d.ts +2 -2
- package/src/core/math/noise/create_simplex_noise_2d.d.ts.map +1 -1
- package/src/core/math/noise/create_simplex_noise_2d.js +1 -1
- package/src/core/math/physics/mie/MIE_PARTICLES_STANDARD.js +1 -1
- package/src/core/math/physics/mie/compute_mie_particle_properties_rgb.d.ts +3 -3
- package/src/core/math/physics/mie/compute_mie_particle_properties_rgb.d.ts.map +1 -1
- package/src/core/math/physics/mie/compute_mie_particle_properties_rgb.js +2 -2
- package/src/core/math/random/randomSeed.d.ts +13 -0
- package/src/core/math/random/randomSeed.d.ts.map +1 -0
- package/src/core/math/random/randomSeed.js +16 -0
- package/src/core/math/random/seededRandom_Mulberry32.d.ts +3 -2
- package/src/core/math/random/seededRandom_Mulberry32.d.ts.map +1 -1
- package/src/core/math/random/seededRandom_Mulberry32.js +1 -1
- package/src/core/math/spline/spline3_hermite_apply_transform.d.ts +50 -0
- package/src/core/math/spline/spline3_hermite_apply_transform.d.ts.map +1 -0
- package/src/core/math/spline/spline3_hermite_apply_transform.js +107 -0
- package/src/core/math/spline/spline3_hermite_reverse.d.ts +34 -0
- package/src/core/math/spline/spline3_hermite_reverse.d.ts.map +1 -0
- package/src/core/math/spline/spline3_hermite_reverse.js +49 -0
- package/src/core/math/statistics/hammersley_sequence.d.ts +17 -4
- package/src/core/math/statistics/hammersley_sequence.d.ts.map +1 -1
- package/src/core/math/statistics/hammersley_sequence.js +25 -16
- package/src/core/model/ModuleRegistry.d.ts +2 -2
- package/src/core/model/ModuleRegistry.d.ts.map +1 -1
- package/src/core/model/ModuleRegistry.js +1 -1
- package/src/core/model/node-graph/json/serializeNodeGraphToJSON.d.ts +1 -1
- package/src/core/model/node-graph/json/serializeNodeGraphToJSON.js +2 -2
- package/src/core/model/object/objectDeepEquals.d.ts +2 -2
- package/src/core/model/object/objectDeepEquals.d.ts.map +1 -1
- package/src/core/model/object/objectDeepEquals.js +1 -1
- package/src/core/model/reactive/model/ReactiveExpression.d.ts +2 -2
- package/src/core/model/reactive/model/ReactiveExpression.d.ts.map +1 -1
- package/src/core/model/reactive/model/ReactiveExpression.js +1 -1
- package/src/core/model/validate_enum_schema.d.ts +2 -3
- package/src/core/model/validate_enum_schema.d.ts.map +1 -1
- package/src/core/model/validate_enum_schema.js +1 -1
- package/src/core/process/Future.d.ts +3 -3
- package/src/core/process/Future.d.ts.map +1 -1
- package/src/core/process/Future.js +13 -1
- package/src/core/process/PromiseWatcher.d.ts.map +1 -1
- package/src/core/process/PromiseWatcher.js +9 -2
- package/src/core/process/executor/ConcurrentExecutor.d.ts.map +1 -1
- package/src/core/process/executor/ConcurrentExecutor.js +12 -2
- package/src/core/process/task/Task.d.ts +3 -3
- package/src/core/process/task/Task.d.ts.map +1 -1
- package/src/core/process/task/Task.js +2 -2
- package/src/core/process/task/util/countTask.d.ts +2 -2
- package/src/core/process/task/util/countTask.d.ts.map +1 -1
- package/src/core/process/task/util/countTask.js +1 -1
- package/src/core/process/task/util/randomCountTask.d.ts +2 -2
- package/src/core/process/task/util/randomCountTask.d.ts.map +1 -1
- package/src/core/process/task/util/randomCountTask.js +1 -1
- package/src/core/process/worker/WorkerProxy.d.ts.map +1 -1
- package/src/core/process/worker/WorkerProxy.js +5 -2
- package/src/core/process/worker/extractTransferables.d.ts.map +1 -1
- package/src/core/process/worker/extractTransferables.js +4 -1
- package/src/engine/Engine.d.ts +12 -12
- package/src/engine/Engine.d.ts.map +1 -1
- package/src/engine/Engine.js +149 -10
- package/src/engine/animation/Animations.d.ts +4 -4
- package/src/engine/animation/Animations.d.ts.map +1 -1
- package/src/engine/animation/Animations.js +2 -2
- package/src/engine/animation/EntityAnimation.d.ts +5 -5
- package/src/engine/animation/EntityAnimation.d.ts.map +1 -1
- package/src/engine/animation/EntityAnimation.js +2 -2
- package/src/engine/animation/TransitionFunctions.d.ts +1 -4
- package/src/engine/animation/TransitionFunctions.d.ts.map +1 -1
- package/src/engine/animation/TransitionFunctions.js +1 -1
- package/src/engine/animation/Tween.d.ts +4 -4
- package/src/engine/animation/Tween.d.ts.map +1 -1
- package/src/engine/animation/Tween.js +2 -2
- package/src/engine/asset/AssetManager.d.ts +6 -6
- package/src/engine/asset/AssetManager.d.ts.map +1 -1
- package/src/engine/asset/AssetManager.js +1411 -1411
- package/src/engine/asset/AssetRequest.d.ts +4 -4
- package/src/engine/asset/AssetRequest.d.ts.map +1 -1
- package/src/engine/asset/AssetRequest.js +3 -3
- package/src/engine/asset/loaders/ArrayBufferLoader.d.ts.map +1 -1
- package/src/engine/asset/loaders/ArrayBufferLoader.js +7 -0
- package/src/engine/asset/loaders/AssetLoader.d.ts +3 -3
- package/src/engine/asset/loaders/AssetLoader.d.ts.map +1 -1
- package/src/engine/asset/loaders/AssetLoader.js +2 -2
- package/src/engine/control/first-person/FirstPersonPlayerControllerSystem.d.ts +0 -10
- package/src/engine/control/first-person/FirstPersonPlayerControllerSystem.d.ts.map +1 -1
- package/src/engine/ecs/EntityComponentDataset.d.ts +6 -6
- package/src/engine/ecs/EntityComponentDataset.d.ts.map +1 -1
- package/src/engine/ecs/EntityComponentDataset.js +3 -3
- package/src/engine/ecs/EntityManager.d.ts +4 -4
- package/src/engine/ecs/EntityManager.js +4 -4
- package/src/engine/ecs/EntityObserver.d.ts +3 -3
- package/src/engine/ecs/EntityObserver.d.ts.map +1 -1
- package/src/engine/ecs/EntityObserver.js +2 -2
- package/src/engine/ecs/System.d.ts +4 -4
- package/src/engine/ecs/System.d.ts.map +1 -1
- package/src/engine/ecs/System.js +2 -2
- package/src/engine/ecs/dynamic_actions/DynamicActorSystem.d.ts +2 -3
- package/src/engine/ecs/dynamic_actions/DynamicActorSystem.d.ts.map +1 -1
- package/src/engine/ecs/dynamic_actions/DynamicActorSystem.js +1 -1
- package/src/engine/ecs/grid/pick.d.ts +4 -2
- package/src/engine/ecs/grid/pick.d.ts.map +1 -1
- package/src/engine/ecs/grid/pick.js +1 -1
- package/src/engine/ecs/parent/EntityNode.d.ts +4 -4
- package/src/engine/ecs/parent/EntityNode.d.ts.map +1 -1
- package/src/engine/ecs/parent/EntityNode.js +2 -2
- package/src/engine/ecs/speaker/lines/sets/LineSetDescription.d.ts.map +1 -1
- package/src/engine/ecs/speaker/lines/sets/LineSetDescription.js +3 -1
- package/src/engine/ecs/storage/binary/collection/BinaryCollectionDeSerializer.d.ts +2 -2
- package/src/engine/ecs/storage/binary/collection/BinaryCollectionDeSerializer.d.ts.map +1 -1
- package/src/engine/ecs/storage/binary/collection/BinaryCollectionDeSerializer.js +1 -1
- package/src/engine/ecs/storage/binary/collection/BinaryCollectionHeaderCodec.d.ts +1 -2
- package/src/engine/ecs/storage/binary/collection/BinaryCollectionHeaderCodec.d.ts.map +1 -1
- package/src/engine/ecs/systems/AnimationSystem.d.ts +0 -5
- package/src/engine/ecs/systems/AnimationSystem.d.ts.map +1 -1
- package/src/engine/ecs/terrain/ecs/TerrainSystem.d.ts +2 -2
- package/src/engine/ecs/terrain/ecs/TerrainSystem.d.ts.map +1 -1
- package/src/engine/ecs/terrain/ecs/TerrainSystem.js +5 -2
- package/src/engine/ecs/terrain/tiles/TerrainTileManager.d.ts +34 -5
- package/src/engine/ecs/terrain/tiles/TerrainTileManager.d.ts.map +1 -1
- package/src/engine/ecs/terrain/tiles/TerrainTileManager.js +87 -26
- package/src/engine/ecs/terrain/util/obtainTerrain.d.ts +2 -3
- package/src/engine/ecs/terrain/util/obtainTerrain.d.ts.map +1 -1
- package/src/engine/ecs/terrain/util/obtainTerrain.js +1 -1
- package/src/engine/ecs/util/hideEntityGracefully.js +2 -2
- package/src/engine/graphics/CONTEXT_LOSS_RECOVERY_PLAN.md +446 -0
- package/src/engine/graphics/FrameRunner.d.ts +2 -2
- package/src/engine/graphics/FrameRunner.d.ts.map +1 -1
- package/src/engine/graphics/FrameRunner.js +1 -1
- package/src/engine/graphics/GraphicsEngine.d.ts +60 -9
- package/src/engine/graphics/GraphicsEngine.d.ts.map +1 -1
- package/src/engine/graphics/GraphicsEngine.js +197 -25
- package/src/engine/graphics/camera/CameraShake.d.ts +6 -6
- package/src/engine/graphics/camera/CameraShake.d.ts.map +1 -1
- package/src/engine/graphics/context/WebGLContextFailureReason.d.ts +9 -0
- package/src/engine/graphics/context/WebGLContextFailureReason.d.ts.map +1 -0
- package/src/engine/graphics/context/WebGLContextFailureReason.js +17 -0
- package/src/engine/graphics/context/WebGLContextMonitor.d.ts +106 -0
- package/src/engine/graphics/context/WebGLContextMonitor.d.ts.map +1 -0
- package/src/engine/graphics/context/WebGLContextMonitor.js +341 -0
- package/src/engine/graphics/context/WebGLContextState.d.ts +14 -0
- package/src/engine/graphics/context/WebGLContextState.d.ts.map +1 -0
- package/src/engine/graphics/context/WebGLContextState.js +24 -0
- package/src/engine/graphics/context/testWebGLContextLoss.d.ts +2 -0
- package/src/engine/graphics/context/testWebGLContextLoss.d.ts.map +1 -0
- package/src/engine/graphics/context/testWebGLContextLoss.js +696 -0
- package/src/engine/graphics/ecs/camera/CameraSystem.d.ts +2 -2
- package/src/engine/graphics/ecs/camera/CameraSystem.d.ts.map +1 -1
- package/src/engine/graphics/ecs/camera/CameraSystem.js +1 -1
- package/src/engine/graphics/ecs/camera/serialization/CameraSerializationAdapter.d.ts.map +1 -1
- package/src/engine/graphics/ecs/camera/serialization/CameraSerializationAdapter.js +68 -32
- package/src/engine/graphics/ecs/camera/serialization/CameraSerializationUpgrader_0_1.d.ts +14 -0
- package/src/engine/graphics/ecs/camera/serialization/CameraSerializationUpgrader_0_1.d.ts.map +1 -0
- package/src/engine/graphics/ecs/camera/serialization/CameraSerializationUpgrader_0_1.js +41 -0
- package/src/engine/graphics/ecs/decal/v2/FPDecalSystem.d.ts +2 -3
- package/src/engine/graphics/ecs/decal/v2/FPDecalSystem.d.ts.map +1 -1
- package/src/engine/graphics/ecs/decal/v2/FPDecalSystem.js +2 -2
- package/src/engine/graphics/ecs/light/three/ThreeLightCache.d.ts +1 -1
- package/src/engine/graphics/ecs/light/three/ThreeLightCache.js +1 -1
- package/src/engine/graphics/ecs/mesh-v2/ShadedGeometrySystem.d.ts +4 -5
- package/src/engine/graphics/ecs/mesh-v2/ShadedGeometrySystem.d.ts.map +1 -1
- package/src/engine/graphics/ecs/mesh-v2/ShadedGeometrySystem.js +7 -2
- package/src/engine/graphics/ecs/trail2d/Trail2DSystem.d.ts +1 -1
- package/src/engine/graphics/ecs/trail2d/Trail2DSystem.d.ts.map +1 -1
- package/src/engine/graphics/ecs/trail2d/Trail2DSystem.js +2 -1
- package/src/engine/graphics/ecs/trail3d/Trail3DSystem.d.ts +1 -1
- package/src/engine/graphics/ecs/trail3d/Trail3DSystem.d.ts.map +1 -1
- package/src/engine/graphics/ecs/trail3d/Trail3DSystem.js +3 -5
- package/src/engine/graphics/geometry/MikkT/BuildNeighborsFast.d.ts.map +1 -1
- package/src/engine/graphics/geometry/MikkT/BuildNeighborsFast.js +3 -2
- package/src/engine/graphics/geometry/MikkT/GenerateSharedVerticesIndexList.d.ts.map +1 -1
- package/src/engine/graphics/geometry/MikkT/GenerateSharedVerticesIndexList.js +3 -1
- package/src/engine/graphics/geometry/computeBoundingSphereFromVertexData.d.ts +2 -2
- package/src/engine/graphics/geometry/computeBoundingSphereFromVertexData.js +1 -1
- package/src/engine/graphics/geometry/computeMeshSurfaceArea.js +1 -1
- package/src/engine/graphics/geometry/instancing/InstancedMeshGroup.d.ts +2 -2
- package/src/engine/graphics/geometry/instancing/InstancedMeshGroup.d.ts.map +1 -1
- package/src/engine/graphics/geometry/instancing/InstancedMeshGroup.js +1 -1
- package/src/engine/graphics/geometry/ribbon/Ribbon.d.ts +2 -2
- package/src/engine/graphics/geometry/ribbon/Ribbon.d.ts.map +1 -1
- package/src/engine/graphics/geometry/ribbon/Ribbon.js +1 -1
- package/src/engine/graphics/load_and_set_cubemap_v0.d.ts +5 -0
- package/src/engine/graphics/load_and_set_cubemap_v0.d.ts.map +1 -1
- package/src/engine/graphics/load_and_set_cubemap_v0.js +26 -21
- package/src/engine/graphics/particles/particular/engine/emitter/ParticleEmitter.d.ts +2 -2
- package/src/engine/graphics/particles/particular/engine/emitter/ParticleEmitter.d.ts.map +1 -1
- package/src/engine/graphics/particles/particular/engine/emitter/ParticleEmitter.js +1 -1
- package/src/engine/graphics/particles/particular/engine/emitter/computeEmissionFunction.d.ts +2 -2
- package/src/engine/graphics/particles/particular/engine/emitter/computeEmissionFunction.d.ts.map +1 -1
- package/src/engine/graphics/particles/particular/engine/emitter/computeEmissionFunction.js +1 -1
- package/src/engine/graphics/particles/particular/engine/utils/distrubuteParticlesOnMesh.d.ts +2 -2
- package/src/engine/graphics/particles/particular/engine/utils/distrubuteParticlesOnMesh.d.ts.map +1 -1
- package/src/engine/graphics/particles/particular/engine/utils/distrubuteParticlesOnMesh.js +1 -1
- package/src/engine/graphics/particles/particular/group/ParticleGroup.d.ts.map +1 -1
- package/src/engine/graphics/particles/particular/group/ParticleGroup.js +19 -8
- package/src/engine/graphics/particles/particular/group/optimizeCommandQueue.js +1 -1
- package/src/engine/graphics/render/RendererPool.d.ts +4 -4
- package/src/engine/graphics/render/RendererPool.d.ts.map +1 -1
- package/src/engine/graphics/render/RendererPool.js +2 -2
- package/src/engine/graphics/render/frame_graph/FrameGraph.d.ts +5 -4
- package/src/engine/graphics/render/frame_graph/FrameGraph.d.ts.map +1 -1
- package/src/engine/graphics/render/frame_graph/FrameGraph.js +2 -2
- package/src/engine/graphics/render/layers/RenderLayer.d.ts +2 -2
- package/src/engine/graphics/render/layers/RenderLayer.d.ts.map +1 -1
- package/src/engine/graphics/render/layers/RenderLayer.js +1 -1
- package/src/engine/graphics/render/layers/RenderLayerUtils.js +1 -1
- package/src/engine/graphics/sh3/lightmap/bake_lightmap_for_scene.d.ts +3 -1
- package/src/engine/graphics/sh3/lightmap/bake_lightmap_for_scene.d.ts.map +1 -1
- package/src/engine/graphics/sh3/lightmap/bake_lightmap_for_scene.js +681 -681
- package/src/engine/graphics/sh3/lightmap/lightmap_rasterize_triangle.d.ts +2 -2
- package/src/engine/graphics/sh3/lightmap/lightmap_rasterize_triangle.d.ts.map +1 -1
- package/src/engine/graphics/sh3/lightmap/lightmap_rasterize_triangle.js +145 -145
- package/src/engine/graphics/sh3/lpv/PathTracerProbeRenderer.d.ts +2 -1
- package/src/engine/graphics/sh3/lpv/PathTracerProbeRenderer.d.ts.map +1 -1
- package/src/engine/graphics/texture/atlas/AtlasLookupTexture.d.ts.map +1 -1
- package/src/engine/graphics/texture/atlas/AtlasLookupTexture.js +10 -3
- package/src/engine/graphics/texture/atlas/TextureAtlas.d.ts.map +1 -1
- package/src/engine/graphics/texture/atlas/TextureAtlas.js +4 -1
- package/src/engine/graphics/texture/atlas/gpu/WebGLTextureAtlas.d.ts.map +1 -1
- package/src/engine/graphics/texture/atlas/gpu/WebGLTextureAtlas.js +11 -0
- package/src/engine/graphics/texture/cubemap/load_environment_map.d.ts.map +1 -1
- package/src/engine/graphics/texture/cubemap/load_environment_map.js +5 -2
- package/src/engine/graphics/texture/sampler/Sampler2D.d.ts +2 -2
- package/src/engine/graphics/texture/sampler/Sampler2D.d.ts.map +1 -1
- package/src/engine/graphics/texture/sampler/Sampler2D.js +1 -1
- package/src/engine/graphics/texture/sampler/distance/computeSignedDistanceField_Chamfer.d.ts.map +1 -1
- package/src/engine/graphics/texture/sampler/distance/computeSignedDistanceField_Chamfer.js +26 -18
- package/src/engine/graphics/texture/sampler/filter/sampler2d_blur_gaussian.d.ts +15 -2
- package/src/engine/graphics/texture/sampler/filter/sampler2d_blur_gaussian.d.ts.map +1 -1
- package/src/engine/graphics/texture/sampler/filter/sampler2d_blur_gaussian.js +29 -3
- package/src/engine/graphics/texture/sampler/resize/sampler2d_scale_down_lanczos.d.ts +14 -1
- package/src/engine/graphics/texture/sampler/resize/sampler2d_scale_down_lanczos.d.ts.map +1 -1
- package/src/engine/graphics/texture/sampler/resize/sampler2d_scale_down_lanczos.js +148 -171
- package/src/engine/graphics/texture/sampler/sampler2d_combine.d.ts +2 -2
- package/src/engine/graphics/texture/sampler/sampler2d_combine.d.ts.map +1 -1
- package/src/engine/graphics/texture/sampler/sampler2d_combine.js +1 -1
- package/src/engine/graphics/texture/sampler/sampler2d_paint.js +1 -1
- package/src/engine/graphics/texture/sampler/util/bitSet2Sampler2D.js +2 -2
- package/src/engine/graphics/texture/virtual/VirtualTextureTileLoader.d.ts +1 -1
- package/src/engine/graphics/texture/virtual/VirtualTextureTileLoader.js +2 -2
- package/src/engine/grid/obstacle/GridObstacle.d.ts +2 -2
- package/src/engine/grid/obstacle/GridObstacle.d.ts.map +1 -1
- package/src/engine/grid/obstacle/GridObstacle.js +1 -1
- package/src/engine/input/ecs/util/TopDownCameraControllerHelper.d.ts.map +1 -1
- package/src/engine/input/ecs/util/TopDownCameraControllerHelper.js +10 -3
- package/src/engine/intelligence/behavior/ecs/BehaviorComponent.d.ts +2 -2
- package/src/engine/intelligence/behavior/ecs/BehaviorComponent.d.ts.map +1 -1
- package/src/engine/intelligence/behavior/ecs/BehaviorComponent.js +1 -1
- package/src/engine/intelligence/behavior/primitive/ActionBehavior.d.ts +3 -3
- package/src/engine/intelligence/behavior/primitive/ActionBehavior.d.ts.map +1 -1
- package/src/engine/intelligence/behavior/primitive/ActionBehavior.js +1 -1
- package/src/engine/intelligence/behavior/util/RandomDelayBehavior.d.ts +2 -1
- package/src/engine/intelligence/behavior/util/RandomDelayBehavior.d.ts.map +1 -1
- package/src/engine/intelligence/blackboard/Blackboard.d.ts +4 -4
- package/src/engine/intelligence/blackboard/Blackboard.d.ts.map +1 -1
- package/src/engine/intelligence/blackboard/Blackboard.js +2 -2
- package/src/engine/intelligence/mcts/MonteCarlo.d.ts +17 -15
- package/src/engine/intelligence/mcts/MonteCarlo.d.ts.map +1 -1
- package/src/engine/intelligence/mcts/MonteCarlo.js +345 -345
- package/src/engine/intelligence/mcts/StateNode.d.ts +2 -2
- package/src/engine/intelligence/mcts/StateNode.d.ts.map +1 -1
- package/src/engine/intelligence/mcts/StateNode.js +364 -364
- package/src/engine/intelligence/optimization/RandomOptimizer.d.ts +3 -3
- package/src/engine/intelligence/optimization/RandomOptimizer.d.ts.map +1 -1
- package/src/engine/intelligence/optimization/RandomOptimizer.js +2 -2
- package/src/engine/intelligence/optimization/optimize_cyclic_sequence_pairwise.d.ts +2 -2
- package/src/engine/intelligence/optimization/optimize_cyclic_sequence_pairwise.d.ts.map +1 -1
- package/src/engine/intelligence/optimization/optimize_cyclic_sequence_pairwise.js +1 -1
- package/src/engine/knowledge/database/StaticKnowledgeDataTable.d.ts +4 -5
- package/src/engine/knowledge/database/StaticKnowledgeDataTable.d.ts.map +1 -1
- package/src/engine/knowledge/database/StaticKnowledgeDataTable.js +2 -2
- package/src/engine/navigation/ecs/components/PathSerializationUpgrader_2_3.d.ts +14 -0
- package/src/engine/navigation/ecs/components/PathSerializationUpgrader_2_3.d.ts.map +1 -0
- package/src/engine/navigation/ecs/components/PathSerializationUpgrader_2_3.js +38 -0
- package/src/engine/navigation/ecs/path_following/PathFollowingSystem.d.ts +0 -5
- package/src/engine/navigation/ecs/path_following/PathFollowingSystem.d.ts.map +1 -1
- package/src/engine/network/orchestrator/ServerAuthoritativeClient.d.ts.map +1 -1
- package/src/engine/network/orchestrator/ServerAuthoritativeClient.js +433 -425
- package/src/engine/physics/constraint/solve_constraints.d.ts +4 -1
- package/src/engine/physics/constraint/solve_constraints.d.ts.map +1 -1
- package/src/engine/physics/constraint/solve_constraints.js +38 -13
- package/src/engine/physics/contact/ManifoldStore.d.ts +15 -3
- package/src/engine/physics/contact/ManifoldStore.d.ts.map +1 -1
- package/src/engine/physics/contact/ManifoldStore.js +15 -3
- package/src/engine/physics/ecs/Joint.d.ts +7 -4
- package/src/engine/physics/ecs/Joint.d.ts.map +1 -1
- package/src/engine/physics/ecs/Joint.js +7 -4
- package/src/engine/physics/ecs/PhysicsSystem.d.ts +14 -0
- package/src/engine/physics/ecs/PhysicsSystem.d.ts.map +1 -1
- package/src/engine/physics/ecs/PhysicsSystem.js +16 -0
- package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -8
- package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts.map +1 -1
- package/src/engine/physics/fluid/ecs/FluidSystem.d.ts +3 -7
- package/src/engine/physics/fluid/ecs/FluidSystem.d.ts.map +1 -1
- package/src/engine/physics/narrowphase/convex_convex_manifold.d.ts +15 -7
- package/src/engine/physics/narrowphase/convex_convex_manifold.d.ts.map +1 -1
- package/src/engine/physics/narrowphase/convex_convex_manifold.js +33 -10
- package/src/engine/physics/narrowphase/refine_ray_concave.d.ts +6 -2
- package/src/engine/physics/narrowphase/refine_ray_concave.d.ts.map +1 -1
- package/src/engine/physics/narrowphase/refine_ray_concave.js +6 -2
- package/src/engine/physics/narrowphase/refine_ray_hit.d.ts +6 -2
- package/src/engine/physics/narrowphase/refine_ray_hit.d.ts.map +1 -1
- package/src/engine/physics/narrowphase/refine_ray_hit.js +6 -2
- package/src/engine/physics/queries/raycast.d.ts.map +1 -1
- package/src/engine/physics/queries/raycast.js +11 -4
- package/src/engine/save/GameStateLoader.d.ts +2 -2
- package/src/engine/save/GameStateLoader.d.ts.map +1 -1
- package/src/engine/save/GameStateLoader.js +1 -1
- package/src/engine/save/Storage.d.ts +4 -4
- package/src/engine/save/Storage.d.ts.map +1 -1
- package/src/engine/save/Storage.js +2 -2
- package/src/engine/save/storage/IndexedDBStorage.d.ts.map +1 -1
- package/src/engine/save/storage/IndexedDBStorage.js +24 -5
- package/src/engine/simulation/Ticker.d.ts +12 -0
- package/src/engine/simulation/Ticker.d.ts.map +1 -1
- package/src/engine/simulation/Ticker.js +18 -0
- package/src/engine/sound/simulation/AcousticSimulator.d.ts.map +1 -1
- package/src/engine/sound/simulation/AcousticSimulator.js +9 -1
- package/src/engine/sound/simulation/core/VolumeField.d.ts +6 -2
- package/src/engine/sound/simulation/core/VolumeField.d.ts.map +1 -1
- package/src/engine/sound/simulation/core/VolumeField.js +6 -2
- package/src/engine/sound/simulation/probe/AcousticProbeField.d.ts +6 -2
- package/src/engine/sound/simulation/probe/AcousticProbeField.d.ts.map +1 -1
- package/src/engine/sound/simulation/probe/AcousticProbeField.js +6 -2
- package/src/engine/sound/simulation/probe/acoustic_probe_transfer.d.ts +6 -2
- package/src/engine/sound/simulation/probe/acoustic_probe_transfer.d.ts.map +1 -1
- package/src/engine/sound/simulation/probe/acoustic_probe_transfer.js +6 -2
- package/src/engine/sound/simulation/probe/bakeProbeReflectors.d.ts +3 -1
- package/src/engine/sound/simulation/probe/bakeProbeReflectors.d.ts.map +1 -1
- package/src/engine/sound/simulation/probe/bakeProbeReflectors.js +3 -1
- package/src/engine/sound/simulation/prototypeAcousticSolver.js +1652 -1652
- package/src/engine/sound/sopra/util/buildAttenuationCurve.d.ts +2 -2
- package/src/engine/sound/sopra/util/buildAttenuationCurve.d.ts.map +1 -1
- package/src/engine/sound/sopra/util/buildAttenuationCurve.js +40 -40
- package/src/engine/ui/GUIEngine.d.ts.map +1 -1
- package/src/engine/ui/GUIEngine.js +8 -1
- package/src/engine/ui/notification/NotificationManager.d.ts +5 -5
- package/src/engine/ui/notification/NotificationManager.d.ts.map +1 -1
- package/src/engine/ui/notification/NotificationManager.js +4 -4
- package/src/engine/ui/tiles2d/TileGrid.d.ts +2 -2
- package/src/engine/ui/tiles2d/TileGrid.d.ts.map +1 -1
- package/src/engine/ui/tiles2d/TileGrid.js +1 -1
- package/src/generation/filtering/numeric/complex/CellFilterSimplexNoise.d.ts +1 -1
- package/src/generation/filtering/numeric/complex/CellFilterSimplexNoise.js +1 -1
- package/src/generation/grid/generation/road/GridTaskGenerateRoads.js +2 -2
- package/src/generation/grid/generation/util/buildPathFromDistanceMap.d.ts +1 -1
- package/src/generation/grid/generation/util/buildPathFromDistanceMap.js +1 -1
- package/src/generation/markers/actions/placement/MarkerNodeEntityProcessorRandomRotation.d.ts +2 -1
- package/src/generation/markers/actions/placement/MarkerNodeEntityProcessorRandomRotation.d.ts.map +1 -1
- package/src/generation/markers/actions/probability/MarkerNodeActionSelectWeighted.d.ts +2 -1
- package/src/generation/markers/actions/probability/MarkerNodeActionSelectWeighted.d.ts.map +1 -1
- package/src/generation/markers/transform/MarkerNodeTransformRotateRandom.d.ts +2 -1
- package/src/generation/markers/transform/MarkerNodeTransformRotateRandom.d.ts.map +1 -1
- package/src/generation/markers/transform/MarkerNodeTransformerRecordPropertyClosure.d.ts +4 -3
- package/src/generation/markers/transform/MarkerNodeTransformerRecordPropertyClosure.d.ts.map +1 -1
- package/src/generation/markers/transform/MarkerNodeTransformerRecordPropertyClosure.js +1 -1
- package/src/generation/markers/transform/MarkerNodeTransformerRecordUniqueRandomEnum.d.ts +2 -1
- package/src/generation/markers/transform/MarkerNodeTransformerRecordUniqueRandomEnum.d.ts.map +1 -1
- package/src/generation/theme/TerrainTheme.d.ts.map +1 -1
- package/src/generation/theme/TerrainTheme.js +2 -1
- package/src/generation/theme/ThemeEngine.d.ts.map +1 -1
- package/src/generation/theme/ThemeEngine.js +6 -5
- package/src/view/ViewGroup.d.ts.map +1 -1
- package/src/view/ViewGroup.js +9 -7
- package/src/view/common/VirtualListView.d.ts +1 -1
- package/src/view/common/VirtualListView.js +1 -1
- package/src/view/graphics/WebGLContextFailureView.d.ts +19 -0
- package/src/view/graphics/WebGLContextFailureView.d.ts.map +1 -0
- package/src/view/graphics/WebGLContextFailureView.js +76 -0
- package/src/view/minimap/Minimap.d.ts +8 -0
- package/src/view/minimap/Minimap.d.ts.map +1 -1
- package/src/view/minimap/Minimap.js +16 -3
- package/src/view/minimap/dom/MinimapMarkerView.d.ts.map +1 -1
- package/src/view/minimap/dom/MinimapMarkerView.js +3 -1
- package/src/view/tooltip/gml/compiler/GMLReferenceCompiler.d.ts +2 -2
- package/src/view/tooltip/gml/compiler/GMLReferenceCompiler.d.ts.map +1 -1
- package/src/view/tooltip/gml/compiler/GMLReferenceCompiler.js +1 -1
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
# Correctness review — `core/process`, `core/graph`, `core/debug`, `core/time`, `core/clipboard`, `core/dom`, `core/assert.js`
|
|
2
|
+
|
|
3
|
+
Scope reviewed: every non-spec source file under the listed directories.
|
|
4
|
+
Every finding below was reproduced by executing the real module in Node (no mocks) unless marked otherwise.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## core/debug
|
|
9
|
+
|
|
10
|
+
### PROC-1 `anything()` matches nothing and `nothing()` matches everything
|
|
11
|
+
- **Severity**: CRITICAL
|
|
12
|
+
- **File**: `H:/git/moh/app/src/mir-engine/meep/src/core/debug/matchers/IsAnything.js:12`
|
|
13
|
+
- **Defect**: `IsAnything.matches()` returns `false` unconditionally. The matcher whose entire purpose is "accept any value" rejects every value, and `nothing()` (built as `not(anything())`) therefore accepts every value. Both public factories in `matchers.js` are logically inverted.
|
|
14
|
+
- **Failure scenario**: `anything().matches(42)` → `false` (verified). `assert.that(x, 'x', anything())` throws for *every* `x`. Conversely `nothing().matches(42)` → `true` (verified), so `assert.that(x, 'x', nothing())` never triggers — a guard that is supposed to be unreachable silently passes everything.
|
|
15
|
+
- **Fix**: `matches(item, mismatch_description) { return true; }`.
|
|
16
|
+
- **Confidence**: Confirmed
|
|
17
|
+
|
|
18
|
+
### PROC-2 `Matcher.matches(item)` — the documented single-argument form — throws in AllOf / IsIterableContaining / CombinableMatcher
|
|
19
|
+
- **Severity**: MAJOR
|
|
20
|
+
- **File**: `H:/git/moh/app/src/mir-engine/meep/src/core/debug/matchers/AllOf.js:25` (also `IsIterableContaining.js:22`, `CombinableMatcher.js:25`)
|
|
21
|
+
- **Defect**: The base class declares `matches(item, mismatch_description = NullDescription.INSTANCE)` and documents the second parameter as optional. Subclass overrides re-declare the parameter *without* the default (default parameter values are not inherited), then dereference it on the mismatch path. `assert.that` calls `matcher.matches(value)` with one argument, so this is the primary in-repo call site.
|
|
22
|
+
- **Failure scenario**: `allOf(isIn([3,7]), not(equalTo(7))).matches(2)` → `TypeError: Cannot read properties of undefined (reading 'appendDescriptionOf')` instead of `false` (verified). `hasItem(1).matches([])` → `TypeError: ... (reading 'appendText')` (verified). Any `assert.that(v, 'v', allOf(...))` on a failing value surfaces a TypeError instead of the intended assertion message.
|
|
23
|
+
- **Fix**: Give every override the same default: `matches(item, mismatch_description = NullDescription.INSTANCE)`.
|
|
24
|
+
- **Confidence**: Confirmed
|
|
25
|
+
|
|
26
|
+
### PROC-3 `DescribeAs` never performs template substitution
|
|
27
|
+
- **Severity**: MAJOR
|
|
28
|
+
- **File**: `H:/git/moh/app/src/mir-engine/meep/src/core/debug/matchers/DescribeAs.js:42`
|
|
29
|
+
- **Defect**: Three independent errors in `describeTo`. (a) `String.prototype.matchAll` returns an *iterator*, not an array, so `matches.length` is `undefined` and `for (let i = 0; i < undefined; i++)` never executes. (b) `match.groups[1]` reads a named-group bag that is `undefined` for this un-named-group regex — it would throw if the loop ever ran; it should be `match[1]`. (c) `textStart = match[0].length` should be `match.index + match[0].length`, otherwise the tail slice restarts from the wrong offset.
|
|
30
|
+
- **Failure scenario**: `new DescribeAs("value must be %0 or %1", equalTo(1), ["A","B"]).describeTo(d)` writes the literal `value must be %0 or %1` instead of `value must be "A" or "B"` (verified). Reached in production via `nothing(message)`, which routes through `describeAs`.
|
|
31
|
+
- **Fix**: `const matches = Array.from(this.template.matchAll(RX));`, use `match[1]`, and set `textStart = match.index + match[0].length`.
|
|
32
|
+
- **Confidence**: Confirmed
|
|
33
|
+
|
|
34
|
+
### PROC-4 `appendValue(undefined)` renders as `"null"`
|
|
35
|
+
- **Severity**: MINOR
|
|
36
|
+
- **File**: `H:/git/moh/app/src/mir-engine/meep/src/core/debug/BaseDescription.js:39`
|
|
37
|
+
- **Defect**: `if (value == null)` (loose equality) is true for `undefined` as well as `null`, so the dedicated `else if (value === undefined)` branch on the next line is unreachable.
|
|
38
|
+
- **Failure scenario**: `new InMemoryDescriptor().appendValue(undefined).value` → `"null"` (verified). An assertion failure on an `undefined` value reports it as `null`, which is a different bug class for the reader.
|
|
39
|
+
- **Fix**: Use `value === null` on the first branch, or reorder so the `undefined` test comes first.
|
|
40
|
+
- **Confidence**: Confirmed
|
|
41
|
+
|
|
42
|
+
### PROC-5 `IsIterableContaining` mismatch text describes the collection, not its elements
|
|
43
|
+
- **Severity**: MINOR
|
|
44
|
+
- **File**: `H:/git/moh/app/src/mir-engine/meep/src/core/debug/matchers/IsIterableContaining.js:41`
|
|
45
|
+
- **Defect**: The mismatch loop iterates `itemElement` but passes the whole collection `item` to `describeMismatch`. The element variable is never used.
|
|
46
|
+
- **Failure scenario**: `hasItem(9).matches([1,2,3], d)` produces `mismatches were: [was <1,2,3>, was <1,2,3>, was <1,2,3>]` instead of `was 1, was 2, was 3`.
|
|
47
|
+
- **Fix**: `this.#element_matcher.describeMismatch(itemElement, mismatch_description);`
|
|
48
|
+
- **Confidence**: Confirmed
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## core/process — task & executor
|
|
53
|
+
|
|
54
|
+
### PROC-6 A task blocked on a FAILED dependency is silently dropped: never fails, never resolves
|
|
55
|
+
- **Severity**: CRITICAL
|
|
56
|
+
- **File**: `H:/git/moh/app/src/mir-engine/meep/src/core/process/executor/ConcurrentExecutor.js:286`
|
|
57
|
+
- **Defect**: `resolveTasks()`'s `ResolutionType.FAILED` branch splices the task out of `queueUnresolved` and does nothing else — it never sets `task.state` to `FAILED` and never dispatches `task.on.failed`. The task is left in `INITIAL` forever, invisible to the executor.
|
|
58
|
+
- **Failure scenario**: `A = failingTask(...)`, `B.addDependency(A)`, `executor.run(A); executor.run(B)`. Verified result: `A.state === FAILED (3)`, `B.state === INITIAL (0)`, no `on.failed`/`on.completed` dispatch on B, `executor.contains(B) === false`, `queueUnresolved.length === 0`. `await B.promise()` hangs forever. Worse, any task `C` depending on `B` sees `B.state === INITIAL` in `tryResolve` and returns `UNRESOLVED` forever — the whole downstream subgraph is permanently stuck while `executor.on.completed` still fires.
|
|
59
|
+
- **Fix**: In the FAILED branch, mirror `#fail_task`: set `unresolvedTask.state.set(TaskState.FAILED)` and `unresolvedTask.on.failed.send1(<dependency failure reason>)` before/after the splice, then restart the scan (`i = -1; l = queueUnresolved.length;`) because the dispatch can mutate the queue.
|
|
60
|
+
- **Confidence**: Confirmed
|
|
61
|
+
|
|
62
|
+
### PROC-7 `Task.executeSync()` never updates `Task.state`
|
|
63
|
+
- **Severity**: MAJOR
|
|
64
|
+
- **File**: `H:/git/moh/app/src/mir-engine/meep/src/core/process/task/Task.js:214`
|
|
65
|
+
- **Defect**: `executeSync` runs the initializer, cycles to completion and fires `on.started`/`on.completed`/`on.failed`, but never assigns `this.state`. The task remains in `TaskState.INITIAL` after a successful synchronous run.
|
|
66
|
+
- **Failure scenario**: `const t = emptyTask(); t.executeSync();` → `t.state.getValue() === 0 (INITIAL)`, not `4 (SUCCEEDED)` (verified). Consequently `Task.join`/`t.promise()` takes the "not finished yet" branch and registers `on.completed.addOne(resolve)` on a signal that already fired — the promise never settles (verified: still `PENDING` after 20 ms). Feeding a sync-executed task as a dependency into a `ConcurrentExecutor` also blocks its dependents forever, because `tryResolve` reads `INITIAL` → `UNRESOLVED`.
|
|
67
|
+
- **Fix**: Set `this.state.set(TaskState.RUNNING)` before the cycle loop and `TaskState.SUCCEEDED` / `TaskState.FAILED` alongside the corresponding signal dispatch.
|
|
68
|
+
- **Confidence**: Confirmed
|
|
69
|
+
|
|
70
|
+
### PROC-8 `ignoreFailureTask` does not ignore a *thrown* failure
|
|
71
|
+
- **Severity**: MAJOR
|
|
72
|
+
- **File**: `H:/git/moh/app/src/mir-engine/meep/src/core/process/task/util/ignoreFailureTask.js:38`
|
|
73
|
+
- **Defect**: The wrapper only converts an explicit `TaskSignal.EndFailure` return into `EndSuccess`. An exception thrown out of `task.cycle()` propagates through the wrapper unchanged, and `ConcurrentExecutor.#executeTimeSlice` treats a thrown exception as task failure (`#fail_task`) — so the wrapper fails too. Throwing is a first-class failure mode in this codebase: `failingTask(e)` (the repo's own canonical failing task) fails exclusively by throwing, and both `promiseTask` and `futureTask` `throw error` on rejection.
|
|
74
|
+
- **Failure scenario**: `ignoreFailureTask(failingTask(new Error("boom")))` — the wrapper propagates `Error: boom` (verified) instead of resolving in success as its JSDoc promises. Under `ConcurrentExecutor` the wrapper ends in `FAILED`.
|
|
75
|
+
- **Fix**: Wrap the `task.cycle()` call in try/catch and return `TaskSignal.EndSuccess` on a caught exception (matching the existing treatment of an initializer throw).
|
|
76
|
+
- **Confidence**: Confirmed
|
|
77
|
+
|
|
78
|
+
### PROC-9 `RemainingTimeEstimator.estimate` averages over unfilled sample slots
|
|
79
|
+
- **Severity**: MAJOR
|
|
80
|
+
- **File**: `H:/git/moh/app/src/mir-engine/meep/src/core/process/task/RemainingTimeEstimator.js:39`
|
|
81
|
+
- **Defect**: `rates` is a zero-filled `Float32Array(sampleCount)`; `estimate` always divides the sum by the full `sampleCount` rather than by the number of `update()` calls actually recorded. The unwritten zeros drag the average rate down by a factor of `sampleCount / filled`.
|
|
82
|
+
- **Failure scenario**: `const e = new RemainingTimeEstimator(10); e.update(1000, 0.5); e.estimate(0.5)` → `10000` (verified) when the correct remaining time is `1000`. A 10× over-estimate persists until the ring buffer is fully populated — i.e. for the whole early phase of every task, which is exactly when an ETA is displayed.
|
|
83
|
+
- **Fix**: Track a `filled` counter (`Math.min(filled + 1, sampleCount)`) in `update` and divide by it in `estimate`; return `Infinity` when `filled === 0`.
|
|
84
|
+
- **Confidence**: Confirmed
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## core/process — undo (`ActionProcessor`)
|
|
89
|
+
|
|
90
|
+
### PROC-10 undo → do → redo crashes: the future branch of history is truncated but its marks are not
|
|
91
|
+
- **Severity**: CRITICAL
|
|
92
|
+
- **File**: `H:/git/moh/app/src/mir-engine/meep/src/core/process/undo/ActionProcessor.js:487`
|
|
93
|
+
- **Defect**: `__unsafe_do` discards the redo branch with `history.crop(0, this.cursor)` but leaves `this.marks` untouched (there is a `// TODO clear future marks` on line 489). Marks whose `index` now exceeds `history.length` become dangling, and `__unsafe_redo` uses those indices as a loop bound.
|
|
94
|
+
- **Failure scenario** (verified end-to-end): `await ap.do(A); await ap.do(B); await ap.mark("m1"); await ap.undo(); await ap.do(C);` leaves `history.length === 1`, `cursor === 1`, `marks === [index 2]`. The next `await ap.redo()` computes `mark = 2`, reads `history.get(1) === undefined` and throws `TypeError: Cannot read properties of undefined (reading 'apply')`. This is the ordinary editor sequence "undo, then do something else, then press redo".
|
|
95
|
+
- **Fix**: In `__unsafe_do`, before/alongside the crop, splice off every mark with `index > this.cursor` and dispatch `onMarkRemoved` for each.
|
|
96
|
+
- **Confidence**: Confirmed
|
|
97
|
+
|
|
98
|
+
### PROC-11 `doMany` with a failing action never settles and permanently deadlocks the processor
|
|
99
|
+
- **Severity**: CRITICAL
|
|
100
|
+
- **File**: `H:/git/moh/app/src/mir-engine/meep/src/core/process/undo/ActionProcessor.js:439`
|
|
101
|
+
- **Defect**: `new Promise(async (resolve, reject) => { ... await this.__unsafe_do(action) ... resolve(); })` — the classic async-executor antipattern. When `__unsafe_do` rejects, the rejection escapes into the async executor function's own (discarded) promise; `reject` is never called, so the constructed promise `p` stays pending forever. `p` is then stored in `this.__last_promise`, and every subsequent `do`/`undo`/`redo`/`mark`/`navigateTo` starts with `await this.__barrier()` on that never-settling promise.
|
|
102
|
+
- **Failure scenario** (verified): `ap.doMany([goodAction, throwingAction])` — the returned promise is still `PENDING` after 60 ms and an `unhandledRejection` is emitted (fatal in Node ≥ 15 / reported by Jest). A following `await ap.do(anotherAction)` is also still `PENDING` and `history.length` stays at 1: the ActionProcessor is bricked for the remainder of the session.
|
|
103
|
+
- **Fix**: Drop the wrapper — make the body a plain `async` helper (`const p = (async () => { for (...) await this.__unsafe_do(actions[i]); })()`), matching how `do`/`undo`/`redo` already build `p`.
|
|
104
|
+
- **Confidence**: Confirmed
|
|
105
|
+
|
|
106
|
+
### PROC-12 `cleanupHistory` compares a mark *count* against a *byte* limit — neither history limit is ever enforced
|
|
107
|
+
- **Severity**: MAJOR
|
|
108
|
+
- **File**: `H:/git/moh/app/src/mir-engine/meep/src/core/process/undo/ActionProcessor.js:204`
|
|
109
|
+
- **Defect**: `let lastMark = min2(marks.length, this.historyMemoryLimit) - 1;` mixes a mark count with a byte budget (default 268435456), so `lastMark` is always `marks.length - 1` and the subsequent `if (lastMark + 1 < marks.length)` is always false. `historyMarkLimit` is never consulted at all. The memory branch is equally dead: it decrements while `marks[lastMark].memoryUsage > this.historyMemoryUsage`, but a mark's recorded usage is a *past* snapshot and therefore never exceeds the current total. Separately, if the guard ever did fire, `const marksToDrop = lastMark - this.historyMarkLimit` is negative for any realistic history and `marks[marksToDrop]` would be `undefined` → `TypeError` on `.index`.
|
|
110
|
+
- **Failure scenario** (verified): with `ap.historyMarkLimit = 2`, doing 6 actions each followed by a mark leaves `marks.length === 6` and `history.length === 6` — nothing dropped. With `ap.historyMemoryLimit = 3000` and 10 actions of 1000 bytes each, `historyMemoryUsage === 10000` and `history.length === 10`. Both documented caps are inert, so undo history grows without bound for the process lifetime.
|
|
111
|
+
- **Fix**: Compute the mark cut-off from `historyMarkLimit` (`const marksToDrop = marks.length - this.historyMarkLimit`) and, separately, walk marks from the oldest while `historyMemoryUsage - marks[i].memoryUsage > historyMemoryLimit` for the byte cap. Guard `marksToDrop > 0` before indexing.
|
|
112
|
+
- **Confidence**: Confirmed
|
|
113
|
+
|
|
114
|
+
### PROC-13 `dropHistory` destroys the boundary mark that should survive at index 0
|
|
115
|
+
- **Severity**: MINOR
|
|
116
|
+
- **File**: `H:/git/moh/app/src/mir-engine/meep/src/core/process/undo/ActionProcessor.js:134`
|
|
117
|
+
- **Defect**: The cut-off scan uses `marks[i].index <= recordsToDrop`. Records `[0, recordsToDrop)` are removed, so a mark at exactly `index === recordsToDrop` still points at a surviving record (it becomes index 0) and must be kept. The condition should be `<`. `cleanupHistory`'s own `assert.equal(newFirstMark.index, 0, ...)` on line 224 documents that this mark is expected to survive — the two sites contradict each other.
|
|
118
|
+
- **Failure scenario**: history `[A,B,C,D]`, one mark at `index 2`. `dropHistory(2)` leaves history `[C,D]` but removes the mark, so the caller loses the only navigable point in the surviving history.
|
|
119
|
+
- **Fix**: `for (; i < markCount && marks[i].index < recordsToDrop; i++)`.
|
|
120
|
+
- **Confidence**: Confirmed (by reading; the runtime check is blocked by PROC-14's arity issue in dev builds)
|
|
121
|
+
|
|
122
|
+
### PROC-14 `onMarkAdded` and `onMarkRemoved` emit different payload types
|
|
123
|
+
- **Severity**: MINOR
|
|
124
|
+
- **File**: `H:/git/moh/app/src/mir-engine/meep/src/core/process/undo/ActionProcessor.js:196` and `:154`
|
|
125
|
+
- **Defect**: `mark()` dispatches `this.onMarkAdded.send1(description)` (the raw description, verified) while `dropHistory` dispatches `Mark` instances (`dropped_marks.forEach(this.onMarkRemoved.send1, ...)`) and `mark()`'s own eviction path does `this.onMarkRemoved.send1(mark)`. Both signals are typed `Signal<M>` (description) in the field JSDoc. Additionally, `Array.prototype.forEach` invokes the callback with `(element, index, array)`, so `send1` is called with three arguments.
|
|
126
|
+
- **Failure scenario**: A UI keeping an undo-stack list keyed by description adds `"m1"` on `onMarkAdded` but receives `Mark { index: 0, description: "m1" }` on `onMarkRemoved`; the lookup misses and the entry is never removed from the list.
|
|
127
|
+
- **Fix**: Dispatch `mark.description` from both removal sites, and replace the `forEach(send1, signal)` with an explicit loop so exactly one argument is passed.
|
|
128
|
+
- **Confidence**: Confirmed
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## core/process — other
|
|
133
|
+
|
|
134
|
+
### PROC-15 `Future` can reach a terminal state twice; a rejected future can flip to resolved
|
|
135
|
+
- **Severity**: MAJOR
|
|
136
|
+
- **File**: `H:/git/moh/app/src/mir-engine/meep/src/core/process/Future.js:80`
|
|
137
|
+
- **Defect**: `__handleResolution` and `__handleRejection` set `state` unconditionally with no already-settled guard. The `resolve`/`reject` callbacks handed to the resolver can therefore both fire, and the last one wins — violating the settle-once contract that `Future` advertises by mirroring the Promise API (`then`/`catch`, `@extends Promise<T>`).
|
|
138
|
+
- **Failure scenario** (verified): `const f = new Future((res, rej) => { rej(new Error("nope")); res(42); }); f.resolve();` → `f.state === RESOLVED (2)`, and `f.then(onOk, onErr)` invokes `onOk(42)`. A real Promise stays rejected and runs `onErr`. Downstream, `futureTask` polls `resolved`/`rejected` flags set by exactly this `then` registration, so a resolver that rejects-then-resolves turns a failed task into a successful one.
|
|
139
|
+
- **Fix**: Early-return from both handlers unless `this.state === FutureStates.PENDING`.
|
|
140
|
+
- **Confidence**: Confirmed
|
|
141
|
+
|
|
142
|
+
### PROC-16 `WatchDog.bark` disarms a watchdog that its own action re-armed
|
|
143
|
+
- **Severity**: MAJOR
|
|
144
|
+
- **File**: `H:/git/moh/app/src/mir-engine/meep/src/core/process/WatchDog.js:52`
|
|
145
|
+
- **Defect**: `bark()` runs `this.action.call(...)` *first* and only then sets `this.timeoutId = -1`. During the action `isActive()` still reports `true`, so a re-arm from inside the action is either ignored (`start()` no-ops) or scheduled and then orphaned (`kick()` installs a fresh timer whose id is immediately overwritten with `-1`).
|
|
146
|
+
- **Failure scenario** (verified): a watchdog whose action calls `wd.start()` to re-arm itself — the canonical "handle the stall, then keep watching" pattern. After the first bark `isActive() === false` and no second bark ever occurs: the watchdog is silently dead. With `kick()` instead, a timer is left running that `stop()` can no longer cancel (`isActive()` is false), so the action fires again unexpectedly and the `kick()` inside it throws `WatchDog is not active`.
|
|
147
|
+
- **Fix**: Set `this.timeoutId = -1` *before* invoking the action.
|
|
148
|
+
- **Confidence**: Confirmed
|
|
149
|
+
|
|
150
|
+
### PROC-17 `.finally()` chains create unhandled promise rejections
|
|
151
|
+
- **Severity**: MAJOR
|
|
152
|
+
- **File**: `H:/git/moh/app/src/mir-engine/meep/src/core/process/PromiseWatcher.js:45` and `H:/git/moh/app/src/mir-engine/meep/src/core/process/worker/OnDemandWorkerManager.js:135`
|
|
153
|
+
- **Defect**: Both sites call `promise.finally(cb)` and discard the returned promise. `p.finally(cb)` produces a *new* promise that adopts `p`'s rejection; because nothing attaches a handler to it, a rejecting `p` always produces an unhandled rejection — even when the original caller handled `p` correctly.
|
|
154
|
+
- **Failure scenario**: `watcher.add(somePromise)` where `somePromise` later rejects → `unhandledrejection` in the browser; in Node/Jest (Node ≥ 15 default) the process aborts. For `OnDemandWorkerManager.request`, `WorkerProxy.__failAllPending` rejects every in-flight request on worker error, so a single failing worker script turns each queued request into an unhandled rejection on top of the (correctly handled) rejection the caller sees.
|
|
155
|
+
- **Fix**: Suppress the derived promise: `promise.then(cb, cb)`, or `promise.finally(cb).catch(() => {})`.
|
|
156
|
+
- **Confidence**: Confirmed
|
|
157
|
+
|
|
158
|
+
### PROC-18 `CompositeProcess.finalize()` on a running composite leaves children in `Stopped`, not `Finalized`
|
|
159
|
+
- **Severity**: MAJOR
|
|
160
|
+
- **File**: `H:/git/moh/app/src/mir-engine/meep/src/core/process/CompositeProcess.js:27`
|
|
161
|
+
- **Defect**: `finalize()` finalizes children first, then calls `super.finalize()`. `BaseProcess.finalize` sees the composite still in `Running` and calls `this.shutdown()`, which is the *overridden* `CompositeProcess.shutdown` — so every child is shut down a second time, and `BaseProcess.shutdown` unconditionally writes `ProcessState.Stopped` over the child's `Finalized` state.
|
|
162
|
+
- **Failure scenario** (verified): a `CompositeProcess([a, b])` taken through `initialize(); startup(); finalize()` ends with composite = `finalized` but `a` and `b` = `stopped`. Any subsequent lifecycle check that requires children to be `Finalized`, or a second `finalize()`, sees corrupt state; children that release resources in `finalize` are also asked to shut down after they have already torn themselves down.
|
|
163
|
+
- **Failure path**: `finalize()` → children `Finalized` → `super.finalize()` sees `Running` → `this.shutdown()` → `children.forEach(c => c.shutdown())` → children forced back to `Stopped`.
|
|
164
|
+
- **Fix**: Have `CompositeProcess.finalize` shut itself down first (`if (this.__state.getValue() === ProcessState.Running) super.shutdown();`) before finalizing children, or make `BaseProcess.finalize` call `BaseProcess.prototype.shutdown` on itself only.
|
|
165
|
+
- **Confidence**: Confirmed
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
## core/process — worker
|
|
170
|
+
|
|
171
|
+
### PROC-19 `extractTransferables(null)` throws
|
|
172
|
+
- **Severity**: CRITICAL
|
|
173
|
+
- **File**: `H:/git/moh/app/src/mir-engine/meep/src/core/process/worker/extractTransferables.js:24`
|
|
174
|
+
- **Defect**: `typeof null === "object"`, so a `null` value falls past the "not an object, skip" branch, fails `instanceof ArrayBuffer`, and reaches `value.buffer` → TypeError. The inline duplicate of this exact function generated inside `WorkerBuilder.build()` (line 121) *does* have the `obj === null` guard, which shows the omission is unintentional.
|
|
175
|
+
- **Failure scenario** (verified): `extractTransferables({ a: null }, [])` → `TypeError: Cannot read properties of null (reading 'buffer')`. Any worker payload containing a null field — extremely common in serialized game objects — crashes before the message is ever posted. The spec file covers `{}`, buffers, typed arrays and nesting, but never a null.
|
|
176
|
+
- **Fix**: Change the first branch to `if (value === null || typeof value !== "object")`.
|
|
177
|
+
- **Confidence**: Confirmed
|
|
178
|
+
|
|
179
|
+
### PROC-20 A failed `postMessage` drops the request without ever rejecting its promise
|
|
180
|
+
- **Severity**: CRITICAL
|
|
181
|
+
- **File**: `H:/git/moh/app/src/mir-engine/meep/src/core/process/worker/WorkerProxy.js:154`
|
|
182
|
+
- **Defect**: When `trySendMessage` returns `false` (it swallows the `postMessage` exception and logs it), the request is removed from the pending queue but neither `resolve` nor `reject` is invoked. The promise handed to the caller can never settle: it is no longer in the queue, so `__handleMessage` and `__failAllPending` will never find it either.
|
|
183
|
+
- **Failure scenario**: `workerProxy.someMethod(objectContainingAFunctionOrDomNode)` — `postMessage` throws `DataCloneError`, the console shows `failed to send message:`, and `await` on the returned promise hangs for the lifetime of the page. PROC-21 makes this reachable in ordinary use, since the `toJSON` sanitisation that was meant to prevent non-cloneable payloads never runs.
|
|
184
|
+
- **Fix**: Reject the promise on send failure: `reject(new Error(...))` in place of (or alongside) the `array_remove_first`.
|
|
185
|
+
- **Confidence**: Confirmed
|
|
186
|
+
|
|
187
|
+
### PROC-21 `needsSerialization` compares a function against the string `"function"` — `toJSON` is never applied
|
|
188
|
+
- **Severity**: MAJOR
|
|
189
|
+
- **File**: `H:/git/moh/app/src/mir-engine/meep/src/core/process/worker/WorkerProxy.js:40`
|
|
190
|
+
- **Defect**: `value.toJSON === "function"` should be `typeof value.toJSON === "function"`. As written the branch is unreachable, so `$submitRequest` always posts the raw argument and the documented `toJSON` sanitisation path is dead. (`hasOwnProperty('toJSON')` additionally excludes the normal case where `toJSON` lives on the class prototype.)
|
|
191
|
+
- **Failure scenario** (verified with the exact predicate): `needsSerialization({ toJSON() { return { plain: 1 }; } })` → `false`. Passing any engine object that relies on `toJSON` to strip non-cloneable members (signals, DOM handles, functions) to a worker method posts the raw object, which `postMessage` rejects with `DataCloneError` → the request silently disappears per PROC-20.
|
|
192
|
+
- **Fix**: `if (typeof value.toJSON === "function") { return true; }` (drop the `hasOwnProperty` gate so prototype-defined `toJSON` is honoured).
|
|
193
|
+
- **Confidence**: Confirmed
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
## core/graph
|
|
198
|
+
|
|
199
|
+
### PROC-22 `colorizeGraph` builds an asymmetric adjacency matrix from directed edges and returns invalid colourings
|
|
200
|
+
- **Severity**: MAJOR
|
|
201
|
+
- **File**: `H:/git/moh/app/src/mir-engine/meep/src/core/graph/coloring/colorizeGraph.js:38`
|
|
202
|
+
- **Defect**: `graphToMatrix` writes `data[i][other] = 1` only when `edge.validateTransition(node, otherNode)` succeeds. For a `Forward` edge `P → Q` this sets `data[P][Q] = 1` but leaves `data[Q][P] = 0`. Graph colouring is an inherently undirected constraint, and the downstream `colorizing`/`updateNN` conflict elimination reads the matrix by row only, so a conflict recorded in just one direction is missed whenever the *other* endpoint is coloured first.
|
|
203
|
+
- **Failure scenario** (verified): nodes `X, P, Q, R` with `Forward` edges `P→X`, `X→Q`, `X→R`. `colorizeGraph` returns `[1, 1, 2, 2]` — `X` and `P` are adjacent yet share colour 1. `validateGraphColoring(colors, graph)` (which uses undirected `getNeighbours`) returns `false` on the library's own output.
|
|
204
|
+
- **Fix**: Make the matrix symmetric: set both `data[i * V + otherIdx]` and `data[otherIdx * V + i]` for every attached edge, and drop the `validateTransition` gate (or keep it only for genuinely directed colouring variants, which this algorithm is not).
|
|
205
|
+
- **Confidence**: Confirmed
|
|
206
|
+
|
|
207
|
+
### PROC-23 `convert_graph_to_dot_string` silently omits every `Backward` edge
|
|
208
|
+
- **Severity**: MAJOR
|
|
209
|
+
- **File**: `H:/git/moh/app/src/mir-engine/meep/src/core/graph/convert_graph_to_dot_string.js:59`
|
|
210
|
+
- **Defect**: The emission guard is `(direction === Forward || direction === Undirected) && edge.first === node`. `EdgeDirectionType.Backward` satisfies neither disjunct, so a backward edge is skipped when visiting `first` and also skipped when visiting `second` (because `edge.first !== node` there). It is never written out.
|
|
211
|
+
- **Failure scenario** (verified): a graph with nodes `A`, `B` and `graph.createEdge("A", "B", EdgeDirectionType.Backward)` produces DOT containing both node declarations and **zero** `->` lines. The rendered diagram shows two isolated nodes; the edge is invisible.
|
|
212
|
+
- **Fix**: Add a `Backward` case that emits the reversed arrow `nodeId(secondIndex) -> nodeId(i)` once (guarded on `edge.first === node` so it is emitted exactly once).
|
|
213
|
+
- **Confidence**: Confirmed
|
|
214
|
+
|
|
215
|
+
### PROC-24 Laplacian is inconsistent on multigraphs: degree counts parallel edges, adjacency saturates at 1
|
|
216
|
+
- **Severity**: MAJOR
|
|
217
|
+
- **File**: `H:/git/moh/app/src/mir-engine/meep/src/core/graph/graph_compute_adjacency_matrix.js:23` (surfaces in `graph_compute_laplacian_matrix.js:22`)
|
|
218
|
+
- **Defect**: `graph_compute_adjacency_matrix` writes a literal `1` per edge, so parallel edges collapse to a single 1. `graph_compute_degree_matrix` uses `graph.getNodeDegree`, which returns `NodeContainer.getEdgeCount()` and *does* count each parallel edge. `L = D - A` therefore has rows that don't sum to zero — the defining property of a graph Laplacian. Multi-edges are a first-class supported state of `Graph` (`NodeContainer.__neighbors` is explicitly documented as "number of edges to that node").
|
|
219
|
+
- **Failure scenario** (verified): nodes `A`, `B` joined by two parallel edges. `graph_compute_laplacian_matrix` yields `[[2, -1], [-1, 2]]`; row sums are `1`, not `0`. The correct Laplacian is `[[2, -2], [-2, 2]]`. Any spectral use (Fiedler vector, spectral clustering, connectivity count via the zero-eigenvalue multiplicity) reads the wrong matrix.
|
|
220
|
+
- **Fix**: Accumulate instead of assigning in `graph_compute_adjacency_matrix` — `result.setCellValue(a, b, result.getCellValue(a, b) + 1)` for both `(a,b)` and `(b,a)` — and document that the matrix must be zero-filled by the caller (the JSDoc already states it does not zero-fill).
|
|
221
|
+
- **Confidence**: Confirmed
|
|
222
|
+
|
|
223
|
+
### PROC-25 Heap invariant broken after merging edge weights: `bubbleDown` used where the element must bubble **up**
|
|
224
|
+
- **Severity**: MAJOR
|
|
225
|
+
- **File**: `H:/git/moh/app/src/mir-engine/meep/src/core/graph/mn_graph_collapse_weighted_edge.js:69`
|
|
226
|
+
- **Defect**: `mn_graph_coarsen` builds `new BinaryHeap(WeightedEdge.extractNegativeWeight)`. `BinaryHeap` is a min-heap on the score function (`bubbleUp` swaps while `score < parentScore`, `pop` returns `data[0]`), so score `-weight` makes it a max-heap on weight. In the merge branch, `target_edge.weight += b_edge.weight` **increases** the weight, i.e. **decreases** the score, so the element must move toward the root. The code calls only `heap.bubbleDown(heap_index)`, which is a no-op for a decreased score, leaving the element parked below lighter edges.
|
|
227
|
+
- **Failure scenario**: heap contains edges of weight 10 (root) and 3 (a leaf). Collapsing merges 8 into the weight-3 edge, making it weight 11 while it stays at a leaf position. `bubbleDown` finds no smaller child and returns. The next `open_set.pop()` returns the weight-10 edge instead of the weight-11 edge, so `mn_graph_coarsen` no longer collapses heaviest-edge-first — the coarsening order the algorithm is defined by is violated and the resulting partition differs from the specified one.
|
|
228
|
+
- **Fix**: Call the purpose-built `heap.updateElementScore(target_edge)` (it does `bubbleUp` then `bubbleDown` and already handles the not-found case), replacing the manual `indexOf` + `bubbleDown` pair.
|
|
229
|
+
- **Confidence**: Confirmed
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
## core/clipboard
|
|
234
|
+
|
|
235
|
+
### PROC-26 `obtainClipBoard` has an inverted guard and resolves with `undefined` when there is no clipboard
|
|
236
|
+
- **Severity**: MAJOR
|
|
237
|
+
- **File**: `H:/git/moh/app/src/mir-engine/meep/src/core/clipboard/obtainClipBoard.js:8`
|
|
238
|
+
- **Defect**: The permission check runs only `if (navigator.clipboard === undefined)` — i.e. only when there is no clipboard to permit — and the function then returns `navigator.clipboard` regardless, which in that branch is `undefined`. The condition is backwards: permissions should be verified when a clipboard *is* present, and the absence of `navigator.clipboard` should reject so callers can fall back.
|
|
239
|
+
- **Failure scenario**: a context where `navigator.clipboard` is undefined but `navigator.permissions.query({name:"clipboard-read"})` resolves `"granted"` (Chromium exposes the Permissions API independently of the Async Clipboard API). `obtainClipBoard()` **resolves** with `undefined`; `safeClipboardReadText` then takes its success branch and evaluates `navigator.clipboard.readText()` → `TypeError: Cannot read properties of undefined`. The file-picker fallback — the entire reason `safeClipboardReadText`/`safeClipboardWriteText` exist — is never reached, because it is wired to the rejection handler.
|
|
240
|
+
- **Fix**: Invert to `if (navigator.clipboard === undefined) { throw new Error("Clipboard API unavailable"); }` and perform the permission queries afterwards on the clipboard-present path.
|
|
241
|
+
- **Confidence**: Confirmed (by reading; requires a browser to execute)
|
|
242
|
+
|
|
243
|
+
### PROC-27 `safeClipboardReadText` fallback never settles when the user cancels the file picker
|
|
244
|
+
- **Severity**: MINOR
|
|
245
|
+
- **File**: `H:/git/moh/app/src/mir-engine/meep/src/core/clipboard/safeClipboardReadText.js:29`
|
|
246
|
+
- **Defect**: The fallback returns `new Promise((resolve, reject) => openFile(resolve))`. `resolve` is only reachable from `reader.onload`. Cancelling the dialog fires no `change` event at all; a `change` with no file hits `if (!file) { return; }`; and `reader.onerror` is not wired. `reject` is never called on any path. The hidden `<input>` is also left attached to `document.body`.
|
|
247
|
+
- **Failure scenario**: `await safeClipboardReadText()` in a browser without an Async Clipboard API; the user presses Cancel in the file dialog. The awaiting code hangs forever with no error surfaced.
|
|
248
|
+
- **Fix**: Reject on `reader.onerror` and on the `!file` path, and listen for the `cancel` event on the file input (or a window `focus` fallback) to reject; remove the input element in a `finally`.
|
|
249
|
+
- **Confidence**: Confirmed (by reading; requires a browser to execute)
|
|
250
|
+
|
|
251
|
+
---
|
|
252
|
+
|
|
253
|
+
## Areas reviewed and found clean
|
|
254
|
+
|
|
255
|
+
- `core/assert.js` — no correctness defects found.
|
|
256
|
+
- `core/time/current_time_in_seconds.js` — correct ms→s scaling, correct monotonic-source selection.
|
|
257
|
+
- `core/dom/isImageBitmap.js` — correct.
|
|
258
|
+
- `core/process/matcher/*` (the function-style matchers) — all correct.
|
|
259
|
+
- `core/graph/csr/*` — `csr_graph_find_shortest_path` and `csr_graph_build_distance_field` Dijkstra relaxation, lazy-deletion handling and the `out_exit` inheritance chain are correct (a predecessor is always finalised before the vertex that names it, including under zero-weight edges). `CSRGraph` buffer arithmetic, `truncate_adjacency`, `equals` and `hash` all check out.
|
|
260
|
+
- `core/graph/v2/Graph.js` / `NodeContainer.js` — add/remove edge bookkeeping, neighbour reference counting, self-loop handling and the snapshot-based `traverseEdges` are all consistent; `removeNode` correctly avoids mutation-during-iteration.
|
|
261
|
+
- `core/graph/graph_compute_disconnected_clusters.js`, `layout/computeDisconnectedSubGraphs.js` — no double-visits, no missed nodes, multi-edge safe.
|
|
262
|
+
- `core/graph/coloring/colorizeGraphGreedy.js`, `colorizeGraphMCS.js`, `colorizeGraphGreedyWeight.js`, `validateGraphColoring.js` — correct (the greedy comparator is explicit, not a bare `.sort()`).
|
|
263
|
+
- `core/graph/format/graphviz/graphviz_escape_string.js` — escape ordering is correct (backslashes before quotes).
|
|
264
|
+
- `core/graph/metis/native/coarsen/*` — `match_shem` renumbering contract and `create_coarse_graph`'s merge / self-loop-drop / position-reset logic are consistent with each other and with the worst-case sizing.
|
|
265
|
+
- `core/process/filter/*`, `core/process/executor/profile/*`, `core/process/buildPromiseChain.js`, `core/process/delay.js`, `core/process/SimpleLifecycle.js` — no correctness defects found.
|
|
266
|
+
- `WorkerBuilder`'s function-rename regex was suspected of renaming nested `function` declarations; verified it does not (`[^\]]*` spans newlines, so the nested definition is captured verbatim in group 3).
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
# Correctness review — `core/science` + `core/color`
|
|
2
|
+
|
|
3
|
+
Scope: every non-spec source file under
|
|
4
|
+
`meep/src/core/science/` (40 files) and `meep/src/core/color/` (47 files).
|
|
5
|
+
`editor/`, `prototype*.js`, `*.spec.js` (evidence only) and `.d.ts` excluded.
|
|
6
|
+
|
|
7
|
+
All findings below were reproduced by executing the actual modules under node
|
|
8
|
+
(`type: module`, direct file-URL imports). Numbers quoted are measured, not derived.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Verified-correct (no findings)
|
|
13
|
+
|
|
14
|
+
Checked against the cited standard/reference and found exact:
|
|
15
|
+
|
|
16
|
+
* **`science/units`** — all 7 base + 19 derived SI exponent vectors match the SI Brochure
|
|
17
|
+
(F, H, Ω, S, Wb, T, V, W, Pa, J, N, C, Hz, Bq, Gy, Sv, lm, lx, kat all correct).
|
|
18
|
+
`UnitMatrix` composition/inverse/power algebra correct and alias-safe.
|
|
19
|
+
`magnitude_prefix_index` verified exact at every one of the 21 decade boundaries
|
|
20
|
+
(`Math.log10` returns exact integers for all of 1e-30…1e30, so no off-by-one).
|
|
21
|
+
`unit_matrix_to_string` decomposition terminates and produces the documented strings;
|
|
22
|
+
`compute_scalar_power` correctly rejects fractional powers (`m/s` does not render as `Gy^0.5`).
|
|
23
|
+
* **`sRGB_to_linear` / `linear_to_sRGB`** — threshold 0.04045 / 0.0031308, `1/12.92 = 0.0773993808`,
|
|
24
|
+
`1/1.055 = 0.9478672986`, `0.055/1.055 = 0.0521327014`, exponent `1/2.4 = 0.4166666666666667`.
|
|
25
|
+
All correct to ≥1e-10 relative.
|
|
26
|
+
* **`rgb_to_xyz` / `xyz_to_rgb`** — exact sRGB↔XYZ D65 matrices; measured `rgb(1,1,1) → XYZ
|
|
27
|
+
(0.950456, 1.000000, 1.089058)` and an exact round trip.
|
|
28
|
+
* **`xyz_cmf_tabulated` × `D65_spd_tabulated`** integrated over 360–830 nm gives
|
|
29
|
+
chromaticity **xy = (0.31273, 0.32902)** vs the standard D65 (0.31272, 0.32903) — both tables
|
|
30
|
+
are correct and correctly indexed/interpolated. `D65_spd_tabulated(560) === 100` exactly.
|
|
31
|
+
* **`xyz_cmf_wyman`** — all 7 lobe amplitudes/centres/σL/σR match Wyman–Sloan–Shirley 2013 exactly;
|
|
32
|
+
integrated white point xy = (0.31264, 0.32923).
|
|
33
|
+
* **`planckian_radiance`** — `c2 = 1.438776877e-2 m·K` matches CODATA; the `exponent > 80`
|
|
34
|
+
early-out discards at most ~1e-26 of the peak, so it is numerically free.
|
|
35
|
+
* **`xyz_to_hpe`** — exact Hunt-Pointer-Estévez (D65-normalised) matrix.
|
|
36
|
+
* **`tonemap_aces`** — input/output matrices and the RRT+ODT rational fit match Stephen Hill's
|
|
37
|
+
`ACES.hlsl` exactly; the fit denominator has negative discriminant so there is no pole.
|
|
38
|
+
* **`oklab/*`** — `linear_srgb_to_oklab`, `oklab_to_linear_srgb`, `toe`/`toe_inv`,
|
|
39
|
+
`compute_max_saturation` (all three coefficient sets + Halley step), `find_cusp`,
|
|
40
|
+
`find_gamut_intersection`, `linear_srgb_to_okhsv`, `okhsv_to_linear_srgb`,
|
|
41
|
+
`xyz_to_oklab`, `oklab_to_xyz` — all match Björn Ottosson's reference term for term.
|
|
42
|
+
* **`YCbCr`** — Rec.709 full-range coefficients verified algebraically
|
|
43
|
+
(0.5/(1−Kb) = 0.53891, 0.5/(1−Kr) = 0.635 and their inverses 1.5748 / 1.8556 / 0.1873 / 0.4681).
|
|
44
|
+
* **`Color.setHSL`** — matches the Wikipedia `f(n)` formulation bit-for-bit on 6 test hues.
|
|
45
|
+
* **`Color.setHCL` / `setHSI`** — match the Wikipedia luma-chroma-hue and HSI formulations
|
|
46
|
+
(aside from SCI-6 below).
|
|
47
|
+
* **`color_lerp`** — shortest-arc hue wrap `((h1-h0)%1 + 1.5)%1 - 0.5` is correct in both
|
|
48
|
+
directions; alpha is interpolated.
|
|
49
|
+
* `rgb_to_luminance` (Rec.709), `YCoCg`, `rgb2uint24`, `int2rgb`, `hex/*`, `rgb_to_rgbe9995`,
|
|
50
|
+
`rgbe9995_to_rgb`, `sRGB_cmf`, `D65_spd_analytical` — no defects found.
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## Findings
|
|
55
|
+
|
|
56
|
+
### SCI-1 `color_darken` / `color_lighten` / `color_saturate` / `color_desaturate` silently drop the alpha channel
|
|
57
|
+
- **Severity**: MAJOR
|
|
58
|
+
- **File**:
|
|
59
|
+
`H:/git/moh/app/src/mir-engine/meep/src/core/color/operations/color_darken.js:24-32`,
|
|
60
|
+
`.../color_lighten.js:24-32`,
|
|
61
|
+
`.../color_saturate.js:24-32`,
|
|
62
|
+
`.../color_desaturate.js:24-32`
|
|
63
|
+
- **Defect**: each function builds `const r = new Color();` (whose constructor default is `a = 1`)
|
|
64
|
+
and then writes only R, G, B into it via `okhsv_to_linear_srgb(r, ...hsv)`. The input's alpha is
|
|
65
|
+
never copied, so every returned colour is fully opaque.
|
|
66
|
+
- **Failure scenario**:
|
|
67
|
+
`color_darken(new Color(1, 0, 0, 0.25), 0.5).a` → **produced `1`**, expected `0.25`.
|
|
68
|
+
Measured identically for `color_lighten`, `color_saturate`, `color_desaturate`.
|
|
69
|
+
The reference for "expected" is this module family's own convention: the sibling
|
|
70
|
+
`color_lerp.js:44` does `r.a = lerp(color_0.a, color_1.a, _t)` and `color_lerp.spec.js:86`
|
|
71
|
+
asserts it ("alpha is interpolated linearly"). It is also self-inconsistent: the
|
|
72
|
+
`amount === 0` fast path (`color_darken.js:19-22`) returns `input` itself, so
|
|
73
|
+
`color_darken(c, 0).a === 0.25` while `color_darken(c, 1e-9).a === 1` — a discontinuity in
|
|
74
|
+
alpha at `amount → 0` that cannot be intentional.
|
|
75
|
+
- **Fix**: add `r.a = input.a;` before the `return r;` in all four functions.
|
|
76
|
+
- **Confidence**: Confirmed
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
### SCI-2 `Color.prototype.parse` writes alpha outside `set()`, suppressing / corrupting the `onChanged` signal
|
|
81
|
+
- **Severity**: MAJOR
|
|
82
|
+
- **File**: `H:/git/moh/app/src/mir-engine/meep/src/core/color/Color.js:666-676`
|
|
83
|
+
- **Defect**: `parse()` assigns `this.a = c[3]` directly and only afterwards calls
|
|
84
|
+
`setRGB(...)`, which forwards `this.a` (already the *new* value) into `set()`. `set()`
|
|
85
|
+
therefore (a) sees `_a === a` and can take the "no change" early-out, and (b) when it does
|
|
86
|
+
fire, reports the new alpha as the old alpha in the `send8(r,g,b,a,_r,_g,_b,_a)` payload.
|
|
87
|
+
- **Failure scenario**: with a live listener attached (this is not hypothetical —
|
|
88
|
+
`engine/graphics/ecs/light/binding/LightBinding.js:56` and
|
|
89
|
+
`engine/graphics/ecs/decal/v2/FPDecalSystem.js:180` both subscribe to `color.onChanged`,
|
|
90
|
+
and `Light.js:105`, `Decal.js:74`, `TubePathStyle.js:180` all call `color.parse(...)`):
|
|
91
|
+
|
|
92
|
+
1. Alpha-only change is lost entirely.
|
|
93
|
+
```js
|
|
94
|
+
const c = Color.parse('rgba(0,0,0,1)');
|
|
95
|
+
c.onChanged.add(() => fired++);
|
|
96
|
+
c.parse('rgba(0,0,0,0.5)');
|
|
97
|
+
```
|
|
98
|
+
→ `c.a === 0.5` (state did change) but **`fired === 0`**; expected `fired === 1`.
|
|
99
|
+
The bound decal/light is never rebuilt and keeps rendering at the old opacity.
|
|
100
|
+
2. Old-value payload is wrong even when RGB does change.
|
|
101
|
+
`new Color(0,0,0,1).parse('#FF000080')` fires
|
|
102
|
+
`onChanged(1, 0, 0, 0.50196, 0, 0, 0, **0.50196**)`; the 8th argument (previous alpha)
|
|
103
|
+
should be `1`, not `0.50196`.
|
|
104
|
+
- **Fix**: don't touch `this.a` directly; compute the alpha locally and issue one call —
|
|
105
|
+
`const a = typeof c[3] === 'number' ? c[3] : 1; this.set(c[0]/255, c[1]/255, c[2]/255, a);`
|
|
106
|
+
- **Confidence**: Confirmed
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
### SCI-3 `rgb_to_kelvin` collapses the whole 1000–2000 K range to 1000 K
|
|
111
|
+
- **Severity**: MAJOR
|
|
112
|
+
- **File**: `H:/git/moh/app/src/mir-engine/meep/src/core/color/kelvin/rgb_to_kelvin.js:43-71`
|
|
113
|
+
- **Defect**: the bisection solves only on the blue/red ratio `b / r`. In the forward model
|
|
114
|
+
(`kelvin_to_rgb`) blue is identically `0` for every `t <= 20` (i.e. T ≤ 2000 K) and red is
|
|
115
|
+
pinned to `1` below 6500 K, so `b/r === 0` over the entire lower fifth of the supported range.
|
|
116
|
+
The objective is flat, the bisection always takes the `br >= expected` branch, and the search
|
|
117
|
+
walks the upper bound all the way down to `minTemperature`. The green channel, which *does*
|
|
118
|
+
vary monotonically there (0.2293 at 1000 K → 0.4195 at 1500 K → 0.5441 at 2000 K), is never read.
|
|
119
|
+
- **Failure scenario** (round-tripping this module's own forward function, the only defensible
|
|
120
|
+
reference for an inverse):
|
|
121
|
+
| input T | `kelvin_to_rgb` output | `rgb_to_kelvin` result | expected |
|
|
122
|
+
|---|---|---|---|
|
|
123
|
+
| 1500 K | `[1, 0.419469, 0]` | **1000** | ≈1500 (−33.3 %) |
|
|
124
|
+
| 1900 K | `[1, 0.522401, 0]` | **1000** | ≈1900 (−47.4 %) |
|
|
125
|
+
| 2000 K | `[1, 0.544074, 0]` | **1000** | ≈2000 (−50.0 %) |
|
|
126
|
+
|
|
127
|
+
Also against the vendian.org black-body table the file's sibling spec cites:
|
|
128
|
+
`rgb_to_kelvin(Color.parse('#ff4500'))` (the table's 1100 K entry) → **1000**, expected 1100.
|
|
129
|
+
Everything at 3000 K and above round-trips exactly, so this is specifically the b === 0 band.
|
|
130
|
+
`rgb_to_kelvin.spec.js` does not catch it: its only low-temperature case is the 1000 K entry.
|
|
131
|
+
- **Fix**: when the target `b` is 0 (or, more uniformly, always) bisect on a residual that
|
|
132
|
+
includes green — e.g. minimise `(r_t − r)² + (g_t − g)² + (b_t − b)²` over the same bracket, or
|
|
133
|
+
bisect on `g / r` whenever `b === 0`. `g/r` is strictly monotonic over 1000–6500 K, so the
|
|
134
|
+
existing bisection structure works unchanged.
|
|
135
|
+
- **Confidence**: Confirmed
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
### SCI-4 `kelvin_to_rgb` blue cut-off is at t ≤ 20 instead of the cited fit's t ≤ 19, zeroing blue over 1900–2000 K
|
|
140
|
+
- **Severity**: MINOR
|
|
141
|
+
- **File**: `H:/git/moh/app/src/mir-engine/meep/src/core/color/kelvin/kelvin_to_rgb.js:23`
|
|
142
|
+
- **Defect**: `const b = t <= 20 ? 0 : clamp01(...)`. Every other constant in this file is an
|
|
143
|
+
exact /255 transcription of the Bartlett fit at the cited
|
|
144
|
+
`https://www.zombieprototypes.com/?p=210` (verified: `-254.76935184120902/255 = -0.9990954974165059`,
|
|
145
|
+
`0.8274096064007395/255 = 0.0032447435545127036`, `115.67994401066147/255 = 0.453646839257496`),
|
|
146
|
+
but that source gates the blue lobe at `t <= 19`, not `t <= 20`. The extra decade of clamping
|
|
147
|
+
both suppresses real signal and enlarges the step where the lobe switches on.
|
|
148
|
+
- **Failure scenario**: `kelvin_to_rgb(out, 0, 2000)` → `out[2] = 0.000000`; the cited fit gives
|
|
149
|
+
`(-254.76935 + 0.82741·10 + 115.67994·ln 10)/255 = 0.077912` (19.9/255). The vendian.org
|
|
150
|
+
black-body table that `kelvin_to_rgb.spec.js:20` cites as the fit's source data lists 2000 K as
|
|
151
|
+
`#ff8d0b`, i.e. blue = 11/255 = 0.0431 — also non-zero.
|
|
152
|
+
The visible consequence is a hard step from a 1 K input change:
|
|
153
|
+
`T = 2000 → b = 0.000000`, `T = 2001 → b = 0.078398`, a 20/255 discontinuity in the middle of any
|
|
154
|
+
colour-temperature ramp. (The file already blends the 6500/6700 K regime boundary specifically
|
|
155
|
+
"to avoid discontinuity", so continuity is an intended property here.)
|
|
156
|
+
- **Fix**: change the guard to `t <= 19` to match the cited fit; or, for full continuity, clamp at
|
|
157
|
+
the fit's own zero crossing (`t ≈ 18.51`, i.e. `T ≈ 1851 K`) so the lobe starts from 0.
|
|
158
|
+
- **Confidence**: Confirmed
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
### SCI-5 `rgb2uint32` returns a *signed* int32 — negative for every colour with red ≥ 128
|
|
163
|
+
- **Severity**: MINOR
|
|
164
|
+
- **File**: `H:/git/moh/app/src/mir-engine/meep/src/core/color/rgb2uint32.js:17`
|
|
165
|
+
(surfaced via `Color.toUint32()`, `Color.js:489-491`)
|
|
166
|
+
- **Defect**: `return (partial << 8) | (_a & 0xFF);` — `<<` coerces to int32, so as soon as the red
|
|
167
|
+
byte reaches 0x80 the shifted value sets the sign bit and the expression evaluates negative.
|
|
168
|
+
The JSDoc and the function name both promise a uint32, and the two sibling packers in the same
|
|
169
|
+
package do force unsignedness (`YCbCr_to_rgb_uint24.js:24` and `rgb_to_rgbe9995.js:57` both end
|
|
170
|
+
in `>>> 0`).
|
|
171
|
+
- **Failure scenario**:
|
|
172
|
+
`new Color(1, 0, 0, 1).toUint32()` → **`-16776961`**, expected `4278190335` (`0xFF0000FF`).
|
|
173
|
+
`new Color(1, 1, 1, 1).toUint32()` → **`-1`**, expected `4294967295`.
|
|
174
|
+
Bit pattern is right, the numeric value is not: `color.toUint32() === 0xFF0000FF` is `false`,
|
|
175
|
+
and the value serialises to JSON / compares / hashes as a negative number.
|
|
176
|
+
- **Fix**: `return ((partial << 8) | (_a & 0xFF)) >>> 0;`
|
|
177
|
+
- **Note**: `Color.spec.js:47-66` currently encodes the present behaviour (it normalises with
|
|
178
|
+
`>>> 0` at every assertion and comments on it), so this is a deliberate-looking wart rather
|
|
179
|
+
than an unnoticed one — but it still contradicts the published contract, and the fix would let
|
|
180
|
+
those `>>> 0`s be dropped from the spec.
|
|
181
|
+
- **Confidence**: Confirmed
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
### SCI-6 `Color.setHCL` / `Color.setHSI` produce NaN for infinitesimally negative hues
|
|
186
|
+
- **Severity**: MINOR
|
|
187
|
+
- **File**: `H:/git/moh/app/src/mir-engine/meep/src/core/color/Color.js:332-383` (`setHCL`)
|
|
188
|
+
and `Color.js:393-447` (`setHSI`)
|
|
189
|
+
- **Defect**: both normalise hue as `_h = h % 1; if (_h < 0) _h += Math.ceil(Math.abs(_h));`
|
|
190
|
+
— the modulo happens *before* the shift. For `-1.1e-16 < h < 0` the addition rounds to exactly
|
|
191
|
+
`1.0`, so `h_face = 6`, `Math.floor(h_face) === 6`, and the `switch` (cases 0–5, no `default`)
|
|
192
|
+
leaves `r`, `g`, `b` as `undefined`. `undefined + m` is `NaN`.
|
|
193
|
+
`hsv/hsv2rgb_float.js:11-17` does the same normalisation in the opposite order (shift, then
|
|
194
|
+
`% 1`) and is immune, which shows the correct ordering is already known in this package.
|
|
195
|
+
- **Failure scenario**: `new Color().setHCL(-1e-17, 1, 0.5)` →
|
|
196
|
+
in a production build (asserts stripped) sets `r = g = b = NaN`; expected `(1.2, 0.2, 0.2)`,
|
|
197
|
+
the value produced for `h = 0`. Identical for `setHSI(-1e-17, 1, 0.5)`
|
|
198
|
+
(expected `(1.5, 0, 0)`). Confirmed at `h ∈ {-1e-17, -1e-18, -5e-17}`; `h = -1e-16` and larger
|
|
199
|
+
magnitudes are unaffected.
|
|
200
|
+
- **Fix**: apply the modulo last, exactly as `hsv2rgb_float` does:
|
|
201
|
+
`let _h = h; if (_h < 0) { _h += Math.ceil(Math.abs(_h)); } _h = _h % 1;`
|
|
202
|
+
- **Confidence**: Confirmed
|
|
203
|
+
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
### SCI-7 3-digit / 4-digit CSS hex shorthand silently yields a NaN channel
|
|
207
|
+
- **Severity**: MINOR
|
|
208
|
+
- **File**: `H:/git/moh/app/src/mir-engine/meep/src/core/color/hex/hex2rgb.js:12-14`
|
|
209
|
+
(reached via `parse_color.js:53-61` and `Color.parse`, `Color.js:774-780`)
|
|
210
|
+
- **Defect**: `hex2rgb` slices fixed byte pairs at offsets 1/3/5 and never checks the string
|
|
211
|
+
length, so for a 4-character input the blue slice is `""` and `parseInt("", 16)` is `NaN`.
|
|
212
|
+
`parse_color`'s `#`-branch has no length validation either, so it neither expands the
|
|
213
|
+
shorthand nor falls through to its `throw new Error('Failed to decode color string ...')`.
|
|
214
|
+
- **Failure scenario**: `parse_color('#fff')` → **`[255, 15, NaN, 1]`**, expected `[255, 255, 255, 1]`.
|
|
215
|
+
`Color.parse('#fff')` therefore yields `r = 1, g = 0.0588, b = NaN` in a production build
|
|
216
|
+
(in a dev build `Color.set`'s `assert.notNaN` throws instead, which is why this has gone
|
|
217
|
+
unnoticed). `hex2rgb('#fff8')` → `{r: 255, g: 248, b: NaN, a: 255}`.
|
|
218
|
+
`Color.parse` documents `@throws Error if format could not be parsed` and advertises "most CSS
|
|
219
|
+
color representations", so both silent-NaN and no-throw violate the stated contract.
|
|
220
|
+
- **Fix**: in `hex2rgb`, branch on `hex.length`: for 4/5-character input expand each nibble
|
|
221
|
+
(`c → cc`) before `hex2dec`; otherwise keep the current 7/9-character path. Anything else should
|
|
222
|
+
reach `parse_color`'s existing `throw`.
|
|
223
|
+
- **Confidence**: Confirmed
|