@woosh/meep-engine 3.29.0 → 3.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/bundle-worker-image-decoder.js +1 -1
- package/build/bundle-worker-terrain.js +1 -1
- package/package.json +1 -1
- package/src/core/binary/BinaryBuffer.d.ts.map +1 -1
- package/src/core/binary/BinaryBuffer.js +14 -2
- package/src/core/bvh2/bvh3/BVH.d.ts.map +1 -1
- package/src/core/bvh2/bvh3/BVH.js +14 -1
- package/src/core/cache/FrequencySketch.d.ts +3 -1
- package/src/core/cache/FrequencySketch.d.ts.map +1 -1
- package/src/core/cache/FrequencySketch.js +10 -4
- package/src/core/cache/wtinylfu/CacheWTinylfu.d.ts.map +1 -1
- package/src/core/cache/wtinylfu/CacheWTinylfu.js +24 -2
- package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compute_vertex_quadrics.d.ts +20 -0
- package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compute_vertex_quadrics.d.ts.map +1 -1
- package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compute_vertex_quadrics.js +69 -4
- package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_simplify.d.ts +48 -18
- package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_simplify.d.ts.map +1 -1
- package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_simplify.js +262 -70
- package/src/core/geom/3d/topology/struct/binary/query/bt_edge_collapse_fold_limit.d.ts +25 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_edge_collapse_fold_limit.d.ts.map +1 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_edge_collapse_fold_limit.js +228 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_edge_collapse_would_fold.d.ts +13 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_edge_collapse_would_fold.d.ts.map +1 -1
- package/src/core/geom/3d/topology/struct/binary/query/bt_edge_collapse_would_fold.js +10 -128
- package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_build_face_bvh.d.ts.map +1 -1
- package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_build_face_bvh.js +11 -1
- package/src/core/graph/mn_graph_collapse_weighted_edge.js +6 -6
- package/src/core/math/lookup/ParameterLookupTable.d.ts.map +1 -1
- package/src/core/math/lookup/ParameterLookupTable.js +10 -5
- package/src/core/math/noise/sdnoise.d.ts.map +1 -1
- package/src/core/math/noise/sdnoise.js +16 -10
- package/src/core/model/object/ImmutableObjectPool.d.ts.map +1 -1
- package/src/core/model/object/ImmutableObjectPool.js +17 -7
- package/src/core/process/executor/ConcurrentExecutor.d.ts +1 -5
- package/src/core/process/executor/ConcurrentExecutor.d.ts.map +1 -1
- package/src/core/process/executor/ConcurrentExecutor.js +95 -1
- package/src/core/process/task/task_find_dependency_cycle.d.ts +17 -0
- package/src/core/process/task/task_find_dependency_cycle.d.ts.map +1 -0
- package/src/core/process/task/task_find_dependency_cycle.js +81 -0
- package/src/core/wfc/WFCCellChoice.d.ts +17 -0
- package/src/core/wfc/WFCCellChoice.d.ts.map +1 -0
- package/src/core/wfc/WFCCellChoice.js +36 -0
- package/src/core/wfc/WFCLattice.d.ts +105 -0
- package/src/core/wfc/WFCLattice.d.ts.map +1 -0
- package/src/core/wfc/WFCLattice.js +235 -0
- package/src/core/wfc/WFCRuleTable.d.ts +172 -0
- package/src/core/wfc/WFCRuleTable.d.ts.map +1 -0
- package/src/core/wfc/WFCRuleTable.js +334 -0
- package/src/core/wfc/WFCState.d.ts +273 -0
- package/src/core/wfc/WFCState.d.ts.map +1 -0
- package/src/core/wfc/WFCState.js +552 -0
- package/src/core/wfc/WFCStatus.d.ts +11 -0
- package/src/core/wfc/WFCStatus.d.ts.map +1 -0
- package/src/core/wfc/WFCStatus.js +28 -0
- package/src/core/wfc/WFCTileWeights.d.ts +48 -0
- package/src/core/wfc/WFCTileWeights.d.ts.map +1 -0
- package/src/core/wfc/WFCTileWeights.js +86 -0
- package/src/core/wfc/WFC_UNRESOLVED.d.ts +12 -0
- package/src/core/wfc/WFC_UNRESOLVED.d.ts.map +1 -0
- package/src/core/wfc/WFC_UNRESOLVED.js +11 -0
- package/src/core/wfc/wfc_estimate_bytes.d.ts +24 -0
- package/src/core/wfc/wfc_estimate_bytes.d.ts.map +1 -0
- package/src/core/wfc/wfc_estimate_bytes.js +47 -0
- package/src/core/wfc/wfc_rule_table_tile_components.d.ts +27 -0
- package/src/core/wfc/wfc_rule_table_tile_components.d.ts.map +1 -0
- package/src/core/wfc/wfc_rule_table_tile_components.js +80 -0
- package/src/core/wfc/wfc_solve.d.ts +21 -0
- package/src/core/wfc/wfc_solve.d.ts.map +1 -0
- package/src/core/wfc/wfc_solve.js +154 -0
- package/src/core/wfc/wfc_state_apply_boundary.d.ts +18 -0
- package/src/core/wfc/wfc_state_apply_boundary.d.ts.map +1 -0
- package/src/core/wfc/wfc_state_apply_boundary.js +61 -0
- package/src/core/wfc/wfc_state_assign.d.ts +11 -0
- package/src/core/wfc/wfc_state_assign.d.ts.map +1 -0
- package/src/core/wfc/wfc_state_assign.js +36 -0
- package/src/core/wfc/wfc_state_ban_bits.d.ts +17 -0
- package/src/core/wfc/wfc_state_ban_bits.d.ts.map +1 -0
- package/src/core/wfc/wfc_state_ban_bits.js +81 -0
- package/src/core/wfc/wfc_state_collapse.d.ts +12 -0
- package/src/core/wfc/wfc_state_collapse.d.ts.map +1 -0
- package/src/core/wfc/wfc_state_collapse.js +73 -0
- package/src/core/wfc/wfc_state_flush_dirty.d.ts +12 -0
- package/src/core/wfc/wfc_state_flush_dirty.d.ts.map +1 -0
- package/src/core/wfc/wfc_state_flush_dirty.js +39 -0
- package/src/core/wfc/wfc_state_propagate.d.ts +14 -0
- package/src/core/wfc/wfc_state_propagate.d.ts.map +1 -0
- package/src/core/wfc/wfc_state_propagate.js +155 -0
- package/src/core/wfc/wfc_state_read.d.ts +11 -0
- package/src/core/wfc/wfc_state_read.d.ts.map +1 -0
- package/src/core/wfc/wfc_state_read.js +38 -0
- package/src/core/wfc/wfc_state_restrict.d.ts +18 -0
- package/src/core/wfc/wfc_state_restrict.d.ts.map +1 -0
- package/src/core/wfc/wfc_state_restrict.js +39 -0
- package/src/core/wfc/wfc_state_set_eligible.d.ts +24 -0
- package/src/core/wfc/wfc_state_set_eligible.d.ts.map +1 -0
- package/src/core/wfc/wfc_state_set_eligible.js +49 -0
- package/src/core/wfc/wfc_state_undo_to.d.ts +13 -0
- package/src/core/wfc/wfc_state_undo_to.d.ts.map +1 -0
- package/src/core/wfc/wfc_state_undo_to.js +84 -0
- package/src/core/wfc/wfc_words_per_cell.d.ts +11 -0
- package/src/core/wfc/wfc_words_per_cell.d.ts.map +1 -0
- package/src/core/wfc/wfc_words_per_cell.js +16 -0
- package/src/engine/ecs/EntityComponentDataset.d.ts +8 -0
- package/src/engine/ecs/EntityComponentDataset.d.ts.map +1 -1
- package/src/engine/ecs/EntityComponentDataset.js +37 -14
- package/src/engine/ecs/fow/FogOfWarRevealerSystem.d.ts +8 -1
- package/src/engine/ecs/fow/FogOfWarRevealerSystem.d.ts.map +1 -1
- package/src/engine/ecs/fow/FogOfWarRevealerSystem.js +37 -2
- package/src/engine/ecs/guid/UUID.d.ts.map +1 -1
- package/src/engine/ecs/guid/UUID.js +45 -17
- package/src/engine/ecs/ik/OneBoneSurfaceAlignmentSolver.d.ts.map +1 -1
- package/src/engine/ecs/ik/OneBoneSurfaceAlignmentSolver.js +3 -1
- package/src/engine/ecs/terrain/ecs/cling/ClingToTerrainSystem.d.ts.map +1 -1
- package/src/engine/ecs/terrain/ecs/cling/ClingToTerrainSystem.js +366 -360
- package/src/engine/graphics/texture/sampler/resize/sampler2d_scale_down_linear.d.ts.map +1 -1
- package/src/engine/graphics/texture/sampler/resize/sampler2d_scale_down_linear.js +24 -10
- package/src/engine/grid/grid2transform/GridPosition2TransformSystem.d.ts.map +1 -1
- package/src/engine/grid/grid2transform/GridPosition2TransformSystem.js +24 -5
- package/src/engine/interpolation/Interpoland.d.ts +7 -1
- package/src/engine/interpolation/Interpoland.d.ts.map +1 -1
- package/src/engine/interpolation/Interpoland.js +8 -1
- package/src/engine/interpolation/InterpolationSystem.d.ts.map +1 -1
- package/src/engine/interpolation/InterpolationSystem.js +3 -0
- package/src/engine/interpolation/TransformPoseSerializationAdapter.d.ts +5 -3
- package/src/engine/interpolation/TransformPoseSerializationAdapter.d.ts.map +1 -1
- package/src/engine/interpolation/TransformPoseSerializationAdapter.js +14 -3
- package/src/engine/interpolation/pose_interpoland.d.ts.map +1 -1
- package/src/engine/interpolation/pose_interpoland.js +2 -0
- package/src/engine/network/orchestrator/NetworkPeer.d.ts.map +1 -1
- package/src/engine/network/orchestrator/NetworkPeer.js +15 -10
- package/src/engine/physics/cloth/ecs/ClothWorld.d.ts.map +1 -1
- package/src/engine/physics/cloth/ecs/ClothWorld.js +17 -0
- package/src/engine/physics/mls-mpm/MLS_MPM.d.ts.map +1 -1
- package/src/engine/physics/mls-mpm/MLS_MPM.js +14 -0
- package/src/engine/sound/sopra/definition/clip/AbstractAudioClip.d.ts +2 -1
- package/src/engine/sound/sopra/definition/clip/AbstractAudioClip.d.ts.map +1 -1
- package/src/engine/sound/sopra/definition/clip/AbstractAudioClip.js +1 -1
- package/src/engine/sound/sopra/definition/clip/BlendContainerAudioClip.d.ts +1 -0
- package/src/engine/sound/sopra/definition/clip/BlendContainerAudioClip.d.ts.map +1 -1
- package/src/engine/sound/sopra/definition/clip/RandomContainerAudioClip.d.ts +1 -0
- package/src/engine/sound/sopra/definition/clip/RandomContainerAudioClip.d.ts.map +1 -1
- package/src/engine/sound/sopra/definition/clip/SampleAudioClip.d.ts +2 -1
- package/src/engine/sound/sopra/definition/clip/SampleAudioClip.d.ts.map +1 -1
- package/src/engine/sound/sopra/definition/clip/SampleAudioClip.js +12 -3
- package/src/engine/sound/sopra/definition/clip/SequenceContainerAudioClip.d.ts +1 -0
- package/src/engine/sound/sopra/definition/clip/SequenceContainerAudioClip.d.ts.map +1 -1
- package/src/engine/sound/sopra/definition/clip/SwitchContainerAudioClip.d.ts +1 -0
- package/src/engine/sound/sopra/definition/clip/SwitchContainerAudioClip.d.ts.map +1 -1
- package/src/engine/sound/sopra/runtime/EventInstance.d.ts.map +1 -1
- package/src/engine/sound/sopra/runtime/EventInstance.js +4 -1
- package/src/format/image/avif/av1/decode/decode_coefficients.d.ts.map +1 -1
- package/src/format/image/avif/av1/decode/decode_coefficients.js +248 -259
- package/src/format/image/avif/av1/decode/read_golomb.d.ts +21 -0
- package/src/format/image/avif/av1/decode/read_golomb.d.ts.map +1 -0
- package/src/format/image/avif/av1/decode/read_golomb.js +49 -0
- package/src/format/image/avif/av1/filter/loop_restoration.js +461 -451
- package/src/format/image/avif/av1/util/round2.d.ts +19 -2
- package/src/format/image/avif/av1/util/round2.d.ts.map +1 -1
- package/src/format/image/avif/av1/util/round2.js +51 -27
- package/src/format/image/avif/heif/parse_avif_file.js +24 -0
- package/src/shade/renderer/buffer/table/GPUTypedTable.d.ts.map +1 -1
- package/src/shade/renderer/buffer/table/GPUTypedTable.js +14 -0
- package/src/shade/renderer/geometry/virtual/VIRTUAL_GEOMETRY_DESIGN.md +46 -3
- package/src/shade/renderer/geometry/virtual/WEDGE_PLAN_2026_09_18.md +91 -1
- package/src/shade/renderer/geometry/virtual/build/VGeoBuildOptions.d.ts +0 -13
- package/src/shade/renderer/geometry/virtual/build/VGeoBuildOptions.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/build/VGeoBuildOptions.js +0 -15
- package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_group_step.js +5 -5
- package/src/shade/renderer/geometry/virtual/build/mesh/VGEO_DEFAULT_ATTRIBUTE_WEIGHT_RATIOS.d.ts +11 -1
- package/src/shade/renderer/geometry/virtual/build/mesh/VGEO_DEFAULT_ATTRIBUTE_WEIGHT_RATIOS.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/build/mesh/VGEO_DEFAULT_ATTRIBUTE_WEIGHT_RATIOS.js +11 -1
- package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_compute_vertex_normals.d.ts +18 -9
- package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_compute_vertex_normals.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_compute_vertex_normals.js +20 -26
- package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_normalize_source.d.ts +1 -3
- package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_normalize_source.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_normalize_source.js +2 -5
- package/src/shade/renderer/geometry/virtual/build/vgeo_build.js +1 -1
- package/src/shade/renderer/geometry/virtual/runtime/VGeoResidency.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/runtime/VGeoResidency.js +14 -1
- package/src/shade/renderer/global_illumination/probe/octahedral/GPUProbeAtlas.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe/octahedral/GPUProbeAtlas.js +12 -3
- package/src/shade/renderer/light/environment/rgbe/RGBE_ReadPixels_RLE.d.ts.map +1 -1
- package/src/shade/renderer/light/environment/rgbe/RGBE_ReadPixels_RLE.js +7 -1
- package/src/shade/renderer/lightmap/GPULightMap.d.ts.map +1 -1
- package/src/shade/renderer/lightmap/GPULightMap.js +349 -336
- package/src/shade/renderer/loader/gltf/load_gltf.d.ts.map +1 -1
- package/src/shade/renderer/loader/gltf/load_gltf.js +1137 -1128
- package/src/shade/renderer/particles/DESIGN.md +14 -5
- package/src/shade/renderer/particles/GPUParticleSystem.d.ts +25 -10
- package/src/shade/renderer/particles/GPUParticleSystem.d.ts.map +1 -1
- package/src/shade/renderer/particles/GPUParticleSystem.js +182 -38
- package/src/shade/renderer/particles/graph/groups/simplex_reference.d.ts.map +1 -1
- package/src/shade/renderer/particles/graph/groups/simplex_reference.js +16 -10
- package/src/shade/renderer/particles/graph_particles.d.ts +4 -2
- package/src/shade/renderer/particles/graph_particles.d.ts.map +1 -1
- package/src/shade/renderer/particles/graph_particles.js +8 -2
- package/src/shade/renderer/particles/optimizer/particle_vm_semantics.d.ts.map +1 -1
- package/src/shade/renderer/particles/optimizer/particle_vm_semantics.js +8 -2
- package/src/shade/renderer/particles/optimizer/pass_fold_constants.d.ts.map +1 -1
- package/src/shade/renderer/particles/optimizer/pass_fold_constants.js +10 -4
- package/src/shade/renderer/particles/shaders/shader_particle_init_free_list.d.ts +31 -0
- package/src/shade/renderer/particles/shaders/shader_particle_init_free_list.d.ts.map +1 -0
- package/src/shade/renderer/particles/shaders/shader_particle_init_free_list.js +62 -0
- package/src/shade/renderer/particles/vm/ParticleVMReference.d.ts +13 -0
- package/src/shade/renderer/particles/vm/ParticleVMReference.d.ts.map +1 -1
- package/src/shade/renderer/particles/vm/ParticleVMReference.js +95 -17
- package/src/shade/renderer/rasterize/bucket/rasterization_has_opaque_bucket.d.ts +21 -0
- package/src/shade/renderer/rasterize/bucket/rasterization_has_opaque_bucket.d.ts.map +1 -0
- package/src/shade/renderer/rasterize/bucket/rasterization_has_opaque_bucket.js +28 -0
- package/src/shade/renderer/rasterize/fast/graph_rasterize_scene_fast.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/fast/graph_rasterize_scene_fast.js +19 -2
- package/src/shade/renderer/rasterize/standard/graph_rasterize_scene.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/standard/graph_rasterize_scene.js +19 -2
- package/src/shade/renderer/shader/type/WebGPUStruct.d.ts.map +1 -1
- package/src/shade/renderer/shader/type/WebGPUStruct.js +12 -0
- package/src/shade/renderer/texture/mipmaps/WebGPUMipmapGenerator.d.ts +8 -2
- package/src/shade/renderer/texture/mipmaps/WebGPUMipmapGenerator.d.ts.map +1 -1
- package/src/shade/renderer/texture/mipmaps/WebGPUMipmapGenerator.js +41 -6
- package/src/shade/wgsl/emulator/WGSLJavaScriptCompiler.js +2383 -2356
- package/src/view/tooltip/DomTooltipManager.d.ts +7 -0
- package/src/view/tooltip/DomTooltipManager.d.ts.map +1 -1
- package/src/view/tooltip/DomTooltipManager.js +22 -0
- package/src/view/tooltip/DomTooltipObserver.d.ts.map +1 -1
- package/src/view/tooltip/DomTooltipObserver.js +13 -3
- package/src/shade/renderer/geometry/virtual/build/mesh/VGEO_CREASE_ANGLE.js +0 -45
|
@@ -4,6 +4,7 @@ import { IllegalStateException } from "../../fsm/exceptions/IllegalStateExceptio
|
|
|
4
4
|
import { objectKeyByValue } from "../../model/object/objectKeyByValue.js";
|
|
5
5
|
import { TaskSignal } from "../task/TaskSignal.js";
|
|
6
6
|
import TaskState from "../task/TaskState.js";
|
|
7
|
+
import { task_find_dependency_cycle } from "../task/task_find_dependency_cycle.js";
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* @typedef {import("../task/Task.js").Task} Task
|
|
@@ -294,7 +295,14 @@ class ConcurrentExecutor {
|
|
|
294
295
|
// Dropping it from the queue is not enough: the task still
|
|
295
296
|
// has to reach a terminal state, or its promise never
|
|
296
297
|
// settles and everything awaiting it waits forever.
|
|
297
|
-
|
|
298
|
+
//
|
|
299
|
+
// Nor is the state enough on its own. `Task.join` reads the
|
|
300
|
+
// state only at the moment it is called and otherwise
|
|
301
|
+
// subscribes to `on.failed`, so a caller that takes the
|
|
302
|
+
// promise before submitting the task was left waiting on a
|
|
303
|
+
// signal nothing sent. The executor's other two terminal
|
|
304
|
+
// transitions both dispatch; this one used not to.
|
|
305
|
+
this.#fail_unresolved_task(unresolvedTask);
|
|
298
306
|
|
|
299
307
|
// its own dependants are now resolvable too, and they may
|
|
300
308
|
// sit earlier in the queue, so rescan from the start the
|
|
@@ -368,6 +376,38 @@ class ConcurrentExecutor {
|
|
|
368
376
|
|
|
369
377
|
}
|
|
370
378
|
|
|
379
|
+
/**
|
|
380
|
+
* Fail a task that never got as far as the ready queue, because one of its dependencies failed.
|
|
381
|
+
* @param {Task} task
|
|
382
|
+
* @private
|
|
383
|
+
*/
|
|
384
|
+
#fail_unresolved_task(task) {
|
|
385
|
+
const dependencies = task.dependencies;
|
|
386
|
+
|
|
387
|
+
let culprit = undefined;
|
|
388
|
+
|
|
389
|
+
for (let i = 0; i < dependencies.length; i++) {
|
|
390
|
+
if (dependencies[i].state.getValue() === TaskState.FAILED) {
|
|
391
|
+
culprit = dependencies[i];
|
|
392
|
+
break;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
const reason = new Error(
|
|
397
|
+
`Task '${task.name}' failed because its dependency '${culprit === undefined ? '?' : culprit.name}' failed`
|
|
398
|
+
);
|
|
399
|
+
|
|
400
|
+
task.state.set(TaskState.FAILED);
|
|
401
|
+
task.on.failed.send1(reason);
|
|
402
|
+
|
|
403
|
+
if (!task.on.failed.hasHandlers()) {
|
|
404
|
+
// no one is watching for task failure
|
|
405
|
+
console.warn(`Task '${task.name}' failed.`, reason);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
this.on.task_completed.send1(task);
|
|
409
|
+
}
|
|
410
|
+
|
|
371
411
|
/**
|
|
372
412
|
*
|
|
373
413
|
* @param {Task} task
|
|
@@ -510,9 +550,63 @@ class ConcurrentExecutor {
|
|
|
510
550
|
* kicks the scheduler into action, this is an internal method and should not be called from outside
|
|
511
551
|
* @private
|
|
512
552
|
*/
|
|
553
|
+
/**
|
|
554
|
+
* Fail every task sitting on a dependency cycle, and then everything that depended on one.
|
|
555
|
+
*
|
|
556
|
+
* A cycle is the one form of "nothing can run" that no later `run` can rescue — an unsubmitted
|
|
557
|
+
* dependency is the other, and that one is a legal pattern here, so idleness alone is not the
|
|
558
|
+
* test. Left alone a cycle stopped the executor with no signal at all: `queueReady` never grows,
|
|
559
|
+
* `#executeTimeSlice` is the only place `on.completed` is sent and is never entered, so `join`
|
|
560
|
+
* subscribes to a signal that cannot fire and the task promises never settle.
|
|
561
|
+
* @private
|
|
562
|
+
*/
|
|
563
|
+
#fail_dependency_cycles() {
|
|
564
|
+
for (
|
|
565
|
+
let cycle = task_find_dependency_cycle(this.queueUnresolved);
|
|
566
|
+
cycle !== null;
|
|
567
|
+
cycle = task_find_dependency_cycle(this.queueUnresolved)
|
|
568
|
+
) {
|
|
569
|
+
const description = cycle.map(t => t.name).join(' -> ');
|
|
570
|
+
|
|
571
|
+
const reason = new Error(`Task dependency cycle: ${description} -> ${cycle[0].name}`);
|
|
572
|
+
|
|
573
|
+
console.error(reason.message, cycle.slice());
|
|
574
|
+
|
|
575
|
+
for (let i = 0; i < cycle.length; i++) {
|
|
576
|
+
const task = cycle[i];
|
|
577
|
+
|
|
578
|
+
const index = this.queueUnresolved.indexOf(task);
|
|
579
|
+
|
|
580
|
+
if (index !== -1) {
|
|
581
|
+
this.queueUnresolved.splice(index, 1);
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
// as with any other failure, the task still has to reach a terminal state or its
|
|
585
|
+
// promise never settles and everything awaiting it waits forever
|
|
586
|
+
task.state.set(TaskState.FAILED);
|
|
587
|
+
task.on.failed.send1(reason);
|
|
588
|
+
|
|
589
|
+
this.on.task_completed.send1(task);
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
// the dependants of what just failed are resolvable now, as failures
|
|
593
|
+
this.resolveTasks();
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
if (this.queueReady.length === 0 && this.queueUnresolved.length === 0) {
|
|
597
|
+
// nothing is left to run, and `#executeTimeSlice` — which is what normally announces
|
|
598
|
+
// that — was never entered, so anything waiting on `join` would wait forever
|
|
599
|
+
this.on.completed.send0();
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
|
|
513
603
|
prod() {
|
|
514
604
|
this.resolveTasks();
|
|
515
605
|
|
|
606
|
+
if (!this.busy && this.queueReady.length === 0 && this.queueUnresolved.length > 0) {
|
|
607
|
+
this.#fail_dependency_cycles();
|
|
608
|
+
}
|
|
609
|
+
|
|
516
610
|
if (!this.busy && this.queueReady.length > 0) {
|
|
517
611
|
this.busy = true;
|
|
518
612
|
this.#executeTimeSlice();
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Find one dependency cycle reachable from `roots`, following {@link Task#dependencies}.
|
|
3
|
+
*
|
|
4
|
+
* A cycle can never resolve: every task on it waits for a task that is waiting for it, so all of them
|
|
5
|
+
* stay INITIAL forever. Unlike a dependency that simply has not been submitted yet, no later `run`
|
|
6
|
+
* can rescue it — which is what makes this, and not "the executor is idle with work outstanding", the
|
|
7
|
+
* safe test for a deadlock.
|
|
8
|
+
*
|
|
9
|
+
* Iterative rather than recursive: task graphs are authored by hand but are not bounded in depth, and
|
|
10
|
+
* a stack overflow while diagnosing a hang is not an improvement on the hang.
|
|
11
|
+
*
|
|
12
|
+
* @param {Task[]} roots
|
|
13
|
+
* @returns {Task[]|null} the tasks on a cycle, in dependency order, or null if there is none. The
|
|
14
|
+
* first element depends on the last.
|
|
15
|
+
*/
|
|
16
|
+
export function task_find_dependency_cycle(roots: Task[]): Task[] | null;
|
|
17
|
+
//# sourceMappingURL=task_find_dependency_cycle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"task_find_dependency_cycle.d.ts","sourceRoot":"","sources":["../../../../../src/core/process/task/task_find_dependency_cycle.js"],"names":[],"mappings":"AAkBA;;;;;;;;;;;;;;GAcG;AACH,kDAJW,MAAM,GACJ,MAAM,GAAC,IAAI,CAkDvB"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Not yet visited. Absent from the map, in practice — named for the comparison below.
|
|
3
|
+
* @type {number}
|
|
4
|
+
*/
|
|
5
|
+
const UNVISITED = 0;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* On the current DFS path. An edge back to one of these is a cycle.
|
|
9
|
+
* @type {number}
|
|
10
|
+
*/
|
|
11
|
+
const ON_PATH = 1;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Fully explored, and no cycle was found through it.
|
|
15
|
+
* @type {number}
|
|
16
|
+
*/
|
|
17
|
+
const EXPLORED = 2;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Find one dependency cycle reachable from `roots`, following {@link Task#dependencies}.
|
|
21
|
+
*
|
|
22
|
+
* A cycle can never resolve: every task on it waits for a task that is waiting for it, so all of them
|
|
23
|
+
* stay INITIAL forever. Unlike a dependency that simply has not been submitted yet, no later `run`
|
|
24
|
+
* can rescue it — which is what makes this, and not "the executor is idle with work outstanding", the
|
|
25
|
+
* safe test for a deadlock.
|
|
26
|
+
*
|
|
27
|
+
* Iterative rather than recursive: task graphs are authored by hand but are not bounded in depth, and
|
|
28
|
+
* a stack overflow while diagnosing a hang is not an improvement on the hang.
|
|
29
|
+
*
|
|
30
|
+
* @param {Task[]} roots
|
|
31
|
+
* @returns {Task[]|null} the tasks on a cycle, in dependency order, or null if there is none. The
|
|
32
|
+
* first element depends on the last.
|
|
33
|
+
*/
|
|
34
|
+
export function task_find_dependency_cycle(roots) {
|
|
35
|
+
const states = new Map();
|
|
36
|
+
|
|
37
|
+
const root_count = roots.length;
|
|
38
|
+
|
|
39
|
+
for (let r = 0; r < root_count; r++) {
|
|
40
|
+
const root = roots[r];
|
|
41
|
+
|
|
42
|
+
if (states.get(root) === EXPLORED) {
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const path = [root];
|
|
47
|
+
|
|
48
|
+
//frame `i` holds how many of `path[i]`'s dependencies have been taken
|
|
49
|
+
const cursors = [0];
|
|
50
|
+
|
|
51
|
+
states.set(root, ON_PATH);
|
|
52
|
+
|
|
53
|
+
while (path.length > 0) {
|
|
54
|
+
const depth = path.length - 1;
|
|
55
|
+
const task = path[depth];
|
|
56
|
+
const dependencies = task.dependencies;
|
|
57
|
+
|
|
58
|
+
if (cursors[depth] < dependencies.length) {
|
|
59
|
+
const dependency = dependencies[cursors[depth]];
|
|
60
|
+
|
|
61
|
+
cursors[depth]++;
|
|
62
|
+
|
|
63
|
+
const state = states.get(dependency) ?? UNVISITED;
|
|
64
|
+
|
|
65
|
+
if (state === ON_PATH) {
|
|
66
|
+
return path.slice(path.indexOf(dependency));
|
|
67
|
+
} else if (state === UNVISITED) {
|
|
68
|
+
states.set(dependency, ON_PATH);
|
|
69
|
+
path.push(dependency);
|
|
70
|
+
cursors.push(0);
|
|
71
|
+
}
|
|
72
|
+
} else {
|
|
73
|
+
states.set(task, EXPLORED);
|
|
74
|
+
path.pop();
|
|
75
|
+
cursors.pop();
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which cell the solver collapses next.
|
|
3
|
+
*
|
|
4
|
+
* Affects how reliably a solve finishes and how the output looks. It never affects *which* tile a
|
|
5
|
+
* cell ends up with — that is always a weighted draw — only the order cells are decided in.
|
|
6
|
+
*
|
|
7
|
+
* {@link SCANLINE } is the default and solves rule sets the other two fail on. Reach for
|
|
8
|
+
* {@link ENTROPY } when the character of the output matters more than first-time success.
|
|
9
|
+
* `WFC_DESIGN.md` §4.4 carries the measurements behind that.
|
|
10
|
+
*/
|
|
11
|
+
export type WFCCellChoice = number;
|
|
12
|
+
export namespace WFCCellChoice {
|
|
13
|
+
let ENTROPY: number;
|
|
14
|
+
let MINIMUM_REMAINING: number;
|
|
15
|
+
let SCANLINE: number;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=WFCCellChoice.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"WFCCellChoice.d.ts","sourceRoot":"","sources":["../../../../src/core/wfc/WFCCellChoice.js"],"names":[],"mappings":";;;;;;;;;;4BAWU,MAAM"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which cell the solver collapses next.
|
|
3
|
+
*
|
|
4
|
+
* Affects how reliably a solve finishes and how the output looks. It never affects *which* tile a
|
|
5
|
+
* cell ends up with — that is always a weighted draw — only the order cells are decided in.
|
|
6
|
+
*
|
|
7
|
+
* {@link SCANLINE} is the default and solves rule sets the other two fail on. Reach for
|
|
8
|
+
* {@link ENTROPY} when the character of the output matters more than first-time success.
|
|
9
|
+
* `WFC_DESIGN.md` §4.4 carries the measurements behind that.
|
|
10
|
+
*
|
|
11
|
+
* @readonly
|
|
12
|
+
* @enum {number}
|
|
13
|
+
*/
|
|
14
|
+
export const WFCCellChoice = {
|
|
15
|
+
/**
|
|
16
|
+
* Lowest Shannon entropy over the remaining weights. The only policy in which weights affect
|
|
17
|
+
* the order cells are decided in, which is what gives its output an organic look; it is what
|
|
18
|
+
* Gumin's original and DeBroglie both default to.
|
|
19
|
+
*
|
|
20
|
+
* Needs a priority queue, and fails on rule sets the other two solve.
|
|
21
|
+
*/
|
|
22
|
+
ENTROPY: 0,
|
|
23
|
+
/**
|
|
24
|
+
* Fewest remaining possibilities. Identical to {@link ENTROPY} when every weight is equal, and
|
|
25
|
+
* an integer comparison rather than a logarithm. Needs the same priority queue.
|
|
26
|
+
*/
|
|
27
|
+
MINIMUM_REMAINING: 1,
|
|
28
|
+
/**
|
|
29
|
+
* Ascending cell index, skipping cells already decided. The default: no priority structure,
|
|
30
|
+
* and the policy most likely to solve a given rule set.
|
|
31
|
+
*
|
|
32
|
+
* Its cost is a directional bias in the output. Solving in blocks
|
|
33
|
+
* (see {@link wfc_state_set_eligible}) confines that bias to a block.
|
|
34
|
+
*/
|
|
35
|
+
SCANLINE: 2
|
|
36
|
+
};
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Largest number of axes an {@link WFCLattice} can have.
|
|
3
|
+
*
|
|
4
|
+
* {@link WFCLattice#edge_flags} spends two bits per axis — one per side — in a `u32`.
|
|
5
|
+
* @type {number}
|
|
6
|
+
*/
|
|
7
|
+
export const WFC_MAX_DIMENSION_COUNT: number;
|
|
8
|
+
/**
|
|
9
|
+
* An N-dimensional box of cells with per-axis wrapping. The topology the solver ships with, and
|
|
10
|
+
* the reason dimensionality is a number rather than three separate implementations.
|
|
11
|
+
*
|
|
12
|
+
* `WFCLattice.from_shape([64])` is 1-D, `[16, 16, 16]` is 3-D, `[8, 8, 8, 8]` is 4-D. Cells are
|
|
13
|
+
* numbered row-major — the last axis varies fastest — and {@link index_of} / {@link coordinate_of}
|
|
14
|
+
* convert between a cell index and coordinates.
|
|
15
|
+
*
|
|
16
|
+
* Direction `d` is `axis = d >> 1`, `side = d & 1` (0 toward the lower coordinate), and `d ^ 1` is
|
|
17
|
+
* the opposite direction. There are `2 × dimension_count` of them.
|
|
18
|
+
*
|
|
19
|
+
* At most {@link WFC_MAX_DIMENSION_COUNT} axes, which is what {@link edge_flags} can describe.
|
|
20
|
+
*
|
|
21
|
+
* @author Alex Goldring
|
|
22
|
+
* @copyright Company Named Limited (c) 2026
|
|
23
|
+
*/
|
|
24
|
+
export class WFCLattice {
|
|
25
|
+
/**
|
|
26
|
+
* @param {number[]|Uint32Array} shape
|
|
27
|
+
* @param {boolean[]|Uint8Array} [wrap]
|
|
28
|
+
* @returns {WFCLattice}
|
|
29
|
+
*/
|
|
30
|
+
static from_shape(shape: number[] | Uint32Array, wrap?: boolean[] | Uint8Array): WFCLattice;
|
|
31
|
+
/**
|
|
32
|
+
* Prefer {@link WFCLattice.from_shape}.
|
|
33
|
+
*
|
|
34
|
+
* @param {number[]|Uint32Array} shape extent along each axis, every entry at least 1
|
|
35
|
+
* @param {boolean[]|Uint8Array} [wrap] per axis; defaults to no wrapping
|
|
36
|
+
*/
|
|
37
|
+
constructor(shape: number[] | Uint32Array, wrap?: boolean[] | Uint8Array);
|
|
38
|
+
/** @type {number} */
|
|
39
|
+
dimension_count: number;
|
|
40
|
+
/** @type {number} */
|
|
41
|
+
direction_count: number;
|
|
42
|
+
/** @type {Uint32Array} */
|
|
43
|
+
shape: Uint32Array;
|
|
44
|
+
/** @type {Uint8Array} */
|
|
45
|
+
wrap: Uint8Array;
|
|
46
|
+
/** @type {number} */
|
|
47
|
+
cell_count: number;
|
|
48
|
+
/**
|
|
49
|
+
* Row-major: the last axis is the fastest-varying.
|
|
50
|
+
* @type {Uint32Array}
|
|
51
|
+
*/
|
|
52
|
+
strides: Uint32Array;
|
|
53
|
+
/**
|
|
54
|
+
* `neighbour_offsets[d]` — what to add to a cell index to step in direction `d`, valid
|
|
55
|
+
* only when the cell is not on the face `d` points off.
|
|
56
|
+
* @type {Int32Array}
|
|
57
|
+
*/
|
|
58
|
+
neighbour_offsets: Int32Array;
|
|
59
|
+
/**
|
|
60
|
+
* `wrap_offsets[d]` — what to add instead when the cell *is* on the face `d` points off
|
|
61
|
+
* and that axis wraps.
|
|
62
|
+
*
|
|
63
|
+
* An axis of extent 1 gives an offset of zero, making the cell its own neighbour. That is
|
|
64
|
+
* correct — stepping off a one-cell ring lands back where it started — and it means such a
|
|
65
|
+
* tile must be compatible with itself along that axis.
|
|
66
|
+
* @type {Int32Array}
|
|
67
|
+
*/
|
|
68
|
+
wrap_offsets: Int32Array;
|
|
69
|
+
/**
|
|
70
|
+
* Bit `d` set means the neighbour in direction `d` is outside the lattice.
|
|
71
|
+
* @type {Uint32Array}
|
|
72
|
+
*/
|
|
73
|
+
edge_flags: Uint32Array;
|
|
74
|
+
/**
|
|
75
|
+
* Mark the two faces of every axis.
|
|
76
|
+
*
|
|
77
|
+
* @private
|
|
78
|
+
*/
|
|
79
|
+
private __build_edge_flags;
|
|
80
|
+
/**
|
|
81
|
+
* Cell index of a coordinate — how a caller turns its own coordinates into the cell ids this
|
|
82
|
+
* module speaks in.
|
|
83
|
+
*
|
|
84
|
+
* @param {number[]|Uint32Array} coordinate one entry per axis
|
|
85
|
+
* @returns {number}
|
|
86
|
+
*/
|
|
87
|
+
index_of(coordinate: number[] | Uint32Array): number;
|
|
88
|
+
/**
|
|
89
|
+
* Inverse of {@link index_of}, written into `out`.
|
|
90
|
+
*
|
|
91
|
+
* @param {number[]|Uint32Array} out at least `dimension_count` long
|
|
92
|
+
* @param {number} index
|
|
93
|
+
*/
|
|
94
|
+
coordinate_of(out: number[] | Uint32Array, index: number): void;
|
|
95
|
+
/**
|
|
96
|
+
* The neighbour of `cell` in direction `direction`, or `-1` when there is none — off a
|
|
97
|
+
* non-wrapping face.
|
|
98
|
+
*
|
|
99
|
+
* @param {number} cell
|
|
100
|
+
* @param {number} direction
|
|
101
|
+
* @returns {number} neighbour cell index, or -1
|
|
102
|
+
*/
|
|
103
|
+
neighbour_of(cell: number, direction: number): number;
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=WFCLattice.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"WFCLattice.d.ts","sourceRoot":"","sources":["../../../../src/core/wfc/WFCLattice.js"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,sCAFU,MAAM,CAE0B;AAE1C;;;;;;;;;;;;;;;GAeG;AACH;IAwMI;;;;OAIG;IACH,yBAJW,MAAM,EAAE,GAAC,WAAW,SACpB,OAAO,EAAE,GAAC,UAAU,GAClB,UAAU,CAItB;IA9MD;;;;;OAKG;IACH,mBAHW,MAAM,EAAE,GAAC,WAAW,SACpB,OAAO,EAAE,GAAC,UAAU,EAgG9B;IA9EG,qBAAqB;IACrB,iBADW,MAAM,CACqB;IACtC,qBAAqB;IACrB,iBADW,MAAM,CACyB;IAE1C,0BAA0B;IAC1B,OADW,WAAW,CACuB;IAC7C,yBAAyB;IACzB,MADW,UAAU,CACsB;IAmB3C,qBAAqB;IACrB,YADW,MAAM,CACW;IAE5B;;;OAGG;IACH,SAFU,WAAW,CAE0B;IAQ/C;;;;OAIG;IACH,mBAFU,UAAU,CAEyC;IAE7D;;;;;;;;OAQG;IACH,cAFU,UAAU,CAEoC;IAaxD;;;OAGG;IACH,YAFU,WAAW,CAEwB;IAKjD;;;;OAIG;IACH,2BAmBC;IAED;;;;;;OAMG;IACH,qBAHW,MAAM,EAAE,GAAC,WAAW,GAClB,MAAM,CAiBlB;IAED;;;;;OAKG;IACH,mBAHW,MAAM,EAAE,GAAC,WAAW,SACpB,MAAM,QAgBhB;IAED;;;;;;;OAOG;IACH,mBAJW,MAAM,aACN,MAAM,GACJ,MAAM,CAiBlB;CAUJ"}
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import { assert } from "../assert.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Largest number of axes an {@link WFCLattice} can have.
|
|
5
|
+
*
|
|
6
|
+
* {@link WFCLattice#edge_flags} spends two bits per axis — one per side — in a `u32`.
|
|
7
|
+
* @type {number}
|
|
8
|
+
*/
|
|
9
|
+
export const WFC_MAX_DIMENSION_COUNT = 16;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* An N-dimensional box of cells with per-axis wrapping. The topology the solver ships with, and
|
|
13
|
+
* the reason dimensionality is a number rather than three separate implementations.
|
|
14
|
+
*
|
|
15
|
+
* `WFCLattice.from_shape([64])` is 1-D, `[16, 16, 16]` is 3-D, `[8, 8, 8, 8]` is 4-D. Cells are
|
|
16
|
+
* numbered row-major — the last axis varies fastest — and {@link index_of} / {@link coordinate_of}
|
|
17
|
+
* convert between a cell index and coordinates.
|
|
18
|
+
*
|
|
19
|
+
* Direction `d` is `axis = d >> 1`, `side = d & 1` (0 toward the lower coordinate), and `d ^ 1` is
|
|
20
|
+
* the opposite direction. There are `2 × dimension_count` of them.
|
|
21
|
+
*
|
|
22
|
+
* At most {@link WFC_MAX_DIMENSION_COUNT} axes, which is what {@link edge_flags} can describe.
|
|
23
|
+
*
|
|
24
|
+
* @author Alex Goldring
|
|
25
|
+
* @copyright Company Named Limited (c) 2026
|
|
26
|
+
*/
|
|
27
|
+
export class WFCLattice {
|
|
28
|
+
/**
|
|
29
|
+
* Prefer {@link WFCLattice.from_shape}.
|
|
30
|
+
*
|
|
31
|
+
* @param {number[]|Uint32Array} shape extent along each axis, every entry at least 1
|
|
32
|
+
* @param {boolean[]|Uint8Array} [wrap] per axis; defaults to no wrapping
|
|
33
|
+
*/
|
|
34
|
+
constructor(shape, wrap) {
|
|
35
|
+
assert.isArrayLike(shape, 'shape');
|
|
36
|
+
|
|
37
|
+
if (wrap !== undefined) {
|
|
38
|
+
assert.isArrayLike(wrap, 'wrap');
|
|
39
|
+
assert.greaterThanOrEqual(wrap.length, shape.length, 'wrap.length >= shape.length');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const dimension_count = shape.length;
|
|
43
|
+
|
|
44
|
+
assert.isPositiveInteger(dimension_count, 'shape.length');
|
|
45
|
+
|
|
46
|
+
if (dimension_count > WFC_MAX_DIMENSION_COUNT) {
|
|
47
|
+
throw new Error(`dimension_count(=${dimension_count}) exceeds ${WFC_MAX_DIMENSION_COUNT}; edge_flags holds two bits per axis`);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** @type {number} */
|
|
51
|
+
this.dimension_count = dimension_count;
|
|
52
|
+
/** @type {number} */
|
|
53
|
+
this.direction_count = dimension_count * 2;
|
|
54
|
+
|
|
55
|
+
/** @type {Uint32Array} */
|
|
56
|
+
this.shape = new Uint32Array(dimension_count);
|
|
57
|
+
/** @type {Uint8Array} */
|
|
58
|
+
this.wrap = new Uint8Array(dimension_count);
|
|
59
|
+
|
|
60
|
+
let cell_count = 1;
|
|
61
|
+
|
|
62
|
+
for (let axis = 0; axis < dimension_count; axis++) {
|
|
63
|
+
const extent = shape[axis];
|
|
64
|
+
|
|
65
|
+
assert.isPositiveInteger(extent, 'shape[axis]');
|
|
66
|
+
|
|
67
|
+
this.shape[axis] = extent;
|
|
68
|
+
this.wrap[axis] = (wrap !== undefined && wrap[axis]) ? 1 : 0;
|
|
69
|
+
|
|
70
|
+
cell_count *= extent;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (!Number.isSafeInteger(cell_count)) {
|
|
74
|
+
throw new Error(`cell count overflows: shape ${Array.from(shape).join('x')}`);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** @type {number} */
|
|
78
|
+
this.cell_count = cell_count;
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Row-major: the last axis is the fastest-varying.
|
|
82
|
+
* @type {Uint32Array}
|
|
83
|
+
*/
|
|
84
|
+
this.strides = new Uint32Array(dimension_count);
|
|
85
|
+
|
|
86
|
+
this.strides[dimension_count - 1] = 1;
|
|
87
|
+
|
|
88
|
+
for (let axis = dimension_count - 2; axis >= 0; axis--) {
|
|
89
|
+
this.strides[axis] = this.strides[axis + 1] * this.shape[axis + 1];
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* `neighbour_offsets[d]` — what to add to a cell index to step in direction `d`, valid
|
|
94
|
+
* only when the cell is not on the face `d` points off.
|
|
95
|
+
* @type {Int32Array}
|
|
96
|
+
*/
|
|
97
|
+
this.neighbour_offsets = new Int32Array(this.direction_count);
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* `wrap_offsets[d]` — what to add instead when the cell *is* on the face `d` points off
|
|
101
|
+
* and that axis wraps.
|
|
102
|
+
*
|
|
103
|
+
* An axis of extent 1 gives an offset of zero, making the cell its own neighbour. That is
|
|
104
|
+
* correct — stepping off a one-cell ring lands back where it started — and it means such a
|
|
105
|
+
* tile must be compatible with itself along that axis.
|
|
106
|
+
* @type {Int32Array}
|
|
107
|
+
*/
|
|
108
|
+
this.wrap_offsets = new Int32Array(this.direction_count);
|
|
109
|
+
|
|
110
|
+
for (let axis = 0; axis < dimension_count; axis++) {
|
|
111
|
+
const stride = this.strides[axis];
|
|
112
|
+
const span = stride * (this.shape[axis] - 1);
|
|
113
|
+
|
|
114
|
+
this.neighbour_offsets[axis * 2] = -stride;
|
|
115
|
+
this.neighbour_offsets[axis * 2 + 1] = stride;
|
|
116
|
+
|
|
117
|
+
this.wrap_offsets[axis * 2] = span;
|
|
118
|
+
this.wrap_offsets[axis * 2 + 1] = -span;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Bit `d` set means the neighbour in direction `d` is outside the lattice.
|
|
123
|
+
* @type {Uint32Array}
|
|
124
|
+
*/
|
|
125
|
+
this.edge_flags = new Uint32Array(cell_count);
|
|
126
|
+
|
|
127
|
+
this.__build_edge_flags();
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Mark the two faces of every axis.
|
|
132
|
+
*
|
|
133
|
+
* @private
|
|
134
|
+
*/
|
|
135
|
+
__build_edge_flags() {
|
|
136
|
+
const { cell_count, dimension_count, edge_flags, shape, strides } = this;
|
|
137
|
+
|
|
138
|
+
for (let axis = 0; axis < dimension_count; axis++) {
|
|
139
|
+
const stride = strides[axis];
|
|
140
|
+
const extent = shape[axis];
|
|
141
|
+
const block = stride * extent;
|
|
142
|
+
const high_offset = stride * (extent - 1);
|
|
143
|
+
|
|
144
|
+
const low_bit = 1 << (axis * 2);
|
|
145
|
+
const high_bit = 1 << (axis * 2 + 1);
|
|
146
|
+
|
|
147
|
+
for (let block_start = 0; block_start < cell_count; block_start += block) {
|
|
148
|
+
for (let k = 0; k < stride; k++) {
|
|
149
|
+
edge_flags[block_start + k] |= low_bit;
|
|
150
|
+
edge_flags[block_start + high_offset + k] |= high_bit;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Cell index of a coordinate — how a caller turns its own coordinates into the cell ids this
|
|
158
|
+
* module speaks in.
|
|
159
|
+
*
|
|
160
|
+
* @param {number[]|Uint32Array} coordinate one entry per axis
|
|
161
|
+
* @returns {number}
|
|
162
|
+
*/
|
|
163
|
+
index_of(coordinate) {
|
|
164
|
+
assert.equal(coordinate.length, this.dimension_count, 'coordinate.length === dimension_count');
|
|
165
|
+
|
|
166
|
+
let index = 0;
|
|
167
|
+
|
|
168
|
+
for (let axis = 0; axis < this.dimension_count; axis++) {
|
|
169
|
+
const value = coordinate[axis];
|
|
170
|
+
|
|
171
|
+
assert.isNonNegativeInteger(value, 'coordinate[axis]');
|
|
172
|
+
assert.lessThan(value, this.shape[axis], 'coordinate[axis] < shape[axis]');
|
|
173
|
+
|
|
174
|
+
index += value * this.strides[axis];
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return index;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Inverse of {@link index_of}, written into `out`.
|
|
182
|
+
*
|
|
183
|
+
* @param {number[]|Uint32Array} out at least `dimension_count` long
|
|
184
|
+
* @param {number} index
|
|
185
|
+
*/
|
|
186
|
+
coordinate_of(out, index) {
|
|
187
|
+
assert.isArrayLike(out, 'out');
|
|
188
|
+
assert.greaterThanOrEqual(out.length, this.dimension_count, 'out.length >= dimension_count');
|
|
189
|
+
assert.isNonNegativeInteger(index, 'index');
|
|
190
|
+
assert.lessThan(index, this.cell_count, 'index < cell_count');
|
|
191
|
+
|
|
192
|
+
let rest = index;
|
|
193
|
+
|
|
194
|
+
for (let axis = 0; axis < this.dimension_count; axis++) {
|
|
195
|
+
const stride = this.strides[axis];
|
|
196
|
+
|
|
197
|
+
out[axis] = (rest / stride) | 0;
|
|
198
|
+
rest = rest % stride;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* The neighbour of `cell` in direction `direction`, or `-1` when there is none — off a
|
|
204
|
+
* non-wrapping face.
|
|
205
|
+
*
|
|
206
|
+
* @param {number} cell
|
|
207
|
+
* @param {number} direction
|
|
208
|
+
* @returns {number} neighbour cell index, or -1
|
|
209
|
+
*/
|
|
210
|
+
neighbour_of(cell, direction) {
|
|
211
|
+
assert.isNonNegativeInteger(cell, 'cell');
|
|
212
|
+
assert.isNonNegativeInteger(direction, 'direction');
|
|
213
|
+
assert.lessThan(cell, this.cell_count, 'cell < cell_count');
|
|
214
|
+
assert.lessThan(direction, this.direction_count, 'direction < direction_count');
|
|
215
|
+
|
|
216
|
+
if (((this.edge_flags[cell] >> direction) & 1) === 1) {
|
|
217
|
+
if (this.wrap[direction >> 1] === 0) {
|
|
218
|
+
return -1;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return cell + this.wrap_offsets[direction];
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return cell + this.neighbour_offsets[direction];
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* @param {number[]|Uint32Array} shape
|
|
229
|
+
* @param {boolean[]|Uint8Array} [wrap]
|
|
230
|
+
* @returns {WFCLattice}
|
|
231
|
+
*/
|
|
232
|
+
static from_shape(shape, wrap) {
|
|
233
|
+
return new WFCLattice(shape, wrap);
|
|
234
|
+
}
|
|
235
|
+
}
|