@woosh/meep-engine 3.16.0 → 3.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (410) hide show
  1. package/editor/particles/effect/ParticleEffectDocument.d.ts +21 -6
  2. package/editor/particles/effect/ParticleEffectDocument.d.ts.map +1 -1
  3. package/editor/particles/effect/ParticleEffectDocument.js +38 -17
  4. package/editor/view/node-graph/NodeGraphClipboard.d.ts +186 -0
  5. package/editor/view/node-graph/NodeGraphClipboard.d.ts.map +1 -0
  6. package/editor/view/node-graph/NodeGraphClipboard.js +231 -0
  7. package/editor/view/node-graph/NodeGraphEditorView.d.ts +59 -1
  8. package/editor/view/node-graph/NodeGraphEditorView.d.ts.map +1 -1
  9. package/editor/view/node-graph/NodeGraphEditorView.js +248 -5
  10. package/editor/view/node-graph/NodeGraphView.d.ts.map +1 -1
  11. package/editor/view/node-graph/NodeGraphView.js +84 -8
  12. package/editor/view/node-graph/actions/connection_bind_actions.d.ts +23 -0
  13. package/editor/view/node-graph/actions/connection_bind_actions.d.ts.map +1 -0
  14. package/editor/view/node-graph/actions/connection_bind_actions.js +39 -0
  15. package/editor/view/particles/effect/ParticleCodeEditorView.d.ts.map +1 -1
  16. package/editor/view/particles/effect/ParticleCodeEditorView.js +4 -3
  17. package/editor/view/particles/effect/ParticleCurveEditorView.d.ts +20 -0
  18. package/editor/view/particles/effect/ParticleCurveEditorView.d.ts.map +1 -0
  19. package/editor/view/particles/effect/ParticleCurveEditorView.js +574 -0
  20. package/editor/view/particles/effect/ParticleEmitterInspectorView.d.ts.map +1 -1
  21. package/editor/view/particles/effect/ParticleEmitterInspectorView.js +27 -1
  22. package/editor/view/particles/effect/ParticleGradientEditorView.d.ts +24 -0
  23. package/editor/view/particles/effect/ParticleGradientEditorView.d.ts.map +1 -0
  24. package/editor/view/particles/effect/ParticleGradientEditorView.js +472 -0
  25. package/editor/view/particles/effect/ParticleGraphEditorView.d.ts +5 -0
  26. package/editor/view/particles/effect/ParticleGraphEditorView.d.ts.map +1 -1
  27. package/editor/view/particles/effect/ParticleGraphEditorView.js +61 -31
  28. package/editor/view/particles/effect/ParticleNodeParametersView.d.ts.map +1 -1
  29. package/editor/view/particles/effect/ParticleNodeParametersView.js +33 -0
  30. package/editor/view/particles/effect/particle-editor.css +123 -35
  31. package/package.json +1 -1
  32. package/src/core/binary/allocator/OffsetAllocator.d.ts +25 -16
  33. package/src/core/binary/allocator/OffsetAllocator.d.ts.map +1 -1
  34. package/src/core/binary/allocator/OffsetAllocator.js +10 -1
  35. package/src/core/collection/array/typed/float32_array_hash.d.ts +21 -0
  36. package/src/core/collection/array/typed/float32_array_hash.d.ts.map +1 -0
  37. package/src/core/collection/array/typed/float32_array_hash.js +38 -0
  38. package/src/core/graph/csr/CSRGraph.d.ts +8 -1
  39. package/src/core/graph/csr/CSRGraph.d.ts.map +1 -1
  40. package/src/core/graph/csr/CSRGraph.js +14 -6
  41. package/src/core/model/node-graph/visual/layout/LayoutProblem.d.ts +24 -0
  42. package/src/core/model/node-graph/visual/layout/LayoutProblem.d.ts.map +1 -1
  43. package/src/core/model/node-graph/visual/layout/LayoutProblem.js +27 -0
  44. package/src/core/model/node-graph/visual/layout/NodeGraphLayoutOptions.d.ts +26 -0
  45. package/src/core/model/node-graph/visual/layout/NodeGraphLayoutOptions.d.ts.map +1 -1
  46. package/src/core/model/node-graph/visual/layout/NodeGraphLayoutOptions.js +28 -0
  47. package/src/core/model/node-graph/visual/layout/RouteGrid.d.ts +92 -3
  48. package/src/core/model/node-graph/visual/layout/RouteGrid.d.ts.map +1 -1
  49. package/src/core/model/node-graph/visual/layout/RouteGrid.js +405 -242
  50. package/src/core/model/node-graph/visual/layout/RouteSearch.d.ts +50 -5
  51. package/src/core/model/node-graph/visual/layout/RouteSearch.d.ts.map +1 -1
  52. package/src/core/model/node-graph/visual/layout/RouteSearch.js +160 -24
  53. package/src/core/model/node-graph/visual/layout/RouteSet.d.ts +7 -0
  54. package/src/core/model/node-graph/visual/layout/RouteSet.d.ts.map +1 -1
  55. package/src/core/model/node-graph/visual/layout/RouteSet.js +8 -0
  56. package/src/core/model/node-graph/visual/layout/layout_assign_coordinates.js +9 -3
  57. package/src/core/model/node-graph/visual/layout/layout_build_problem.d.ts.map +1 -1
  58. package/src/core/model/node-graph/visual/layout/layout_build_problem.js +7 -0
  59. package/src/core/model/node-graph/visual/layout/layout_insert_lanes.d.ts +9 -3
  60. package/src/core/model/node-graph/visual/layout/layout_insert_lanes.d.ts.map +1 -1
  61. package/src/core/model/node-graph/visual/layout/layout_insert_lanes.js +17 -3
  62. package/src/core/model/node-graph/visual/layout/layout_node_graph.d.ts +9 -6
  63. package/src/core/model/node-graph/visual/layout/layout_node_graph.d.ts.map +1 -1
  64. package/src/core/model/node-graph/visual/layout/layout_node_graph.js +49 -7
  65. package/src/core/model/node-graph/visual/layout/layout_order_layers.d.ts +11 -1
  66. package/src/core/model/node-graph/visual/layout/layout_order_layers.d.ts.map +1 -1
  67. package/src/core/model/node-graph/visual/layout/layout_order_layers.js +135 -16
  68. package/src/core/model/node-graph/visual/layout/route_connections.d.ts +29 -0
  69. package/src/core/model/node-graph/visual/layout/route_connections.d.ts.map +1 -1
  70. package/src/core/model/node-graph/visual/layout/route_connections.js +716 -309
  71. package/src/core/model/node-graph/visual/layout/route_grid_build.d.ts +3 -2
  72. package/src/core/model/node-graph/visual/layout/route_grid_build.d.ts.map +1 -1
  73. package/src/core/model/node-graph/visual/layout/route_grid_build.js +7 -15
  74. package/src/core/model/node-graph/visual/layout/route_nudge.d.ts +6 -0
  75. package/src/core/model/node-graph/visual/layout/route_nudge.d.ts.map +1 -1
  76. package/src/core/model/node-graph/visual/layout/route_nudge.js +42 -13
  77. package/src/engine/animation/AnimationUtils.js +246 -246
  78. package/src/engine/animation/Animations.js +74 -74
  79. package/src/engine/control/first-person/FirstPersonPlayerControllerSystem.js +2078 -2078
  80. package/src/engine/ecs/EventType.d.ts +0 -1
  81. package/src/engine/ecs/EventType.js +1 -26
  82. package/src/engine/ecs/ik/ik_write_local_rotation.d.ts +1 -1
  83. package/src/engine/ecs/ik/ik_write_local_rotation.d.ts.map +1 -1
  84. package/src/engine/ecs/ik/ik_write_local_rotation.js +3 -7
  85. package/src/engine/ecs/parent/EntityNode.d.ts.map +1 -1
  86. package/src/engine/ecs/parent/EntityNode.js +4 -9
  87. package/src/engine/ecs/terrain/ecs/cling/ClingToTerrainSystem.d.ts.map +1 -1
  88. package/src/engine/ecs/terrain/ecs/cling/ClingToTerrainSystem.js +360 -364
  89. package/src/engine/ecs/transform/TRANSFORM64_EVENT_CHANGE.d.ts +41 -0
  90. package/src/engine/ecs/transform/TRANSFORM64_EVENT_CHANGE.d.ts.map +1 -0
  91. package/src/engine/ecs/transform/TRANSFORM64_EVENT_CHANGE.js +40 -0
  92. package/src/engine/ecs/transform/TRANSFORM64_MIGRATION_PLAN_2026_09_04.md +8 -0
  93. package/src/engine/ecs/transform/t64_announce_change.d.ts +10 -7
  94. package/src/engine/ecs/transform/t64_announce_change.d.ts.map +1 -1
  95. package/src/engine/ecs/transform/t64_announce_change.js +12 -12
  96. package/src/engine/ecs/transform-attachment/TRANSFORM_ATTACHMENT_EVENT_CHANGE.d.ts +30 -0
  97. package/src/engine/ecs/transform-attachment/TRANSFORM_ATTACHMENT_EVENT_CHANGE.d.ts.map +1 -0
  98. package/src/engine/ecs/transform-attachment/TRANSFORM_ATTACHMENT_EVENT_CHANGE.js +29 -0
  99. package/src/engine/ecs/transform-attachment/TransformAttachmentSystem.d.ts.map +1 -1
  100. package/src/engine/ecs/transform-attachment/TransformAttachmentSystem.js +13 -21
  101. package/src/engine/graphics/ecs/sprite/SpriteSystemPE.d.ts.map +1 -1
  102. package/src/engine/graphics/ecs/sprite/SpriteSystemPE.js +4 -8
  103. package/src/engine/graphics3/LightSystem.d.ts.map +1 -1
  104. package/src/engine/graphics3/LightSystem.js +4 -8
  105. package/src/engine/graphics3/MeshSystem.d.ts.map +1 -1
  106. package/src/engine/graphics3/MeshSystem.js +9 -19
  107. package/src/engine/graphics3/ParticleEmitterSystem.d.ts.map +1 -1
  108. package/src/engine/graphics3/ParticleEmitterSystem.js +4 -8
  109. package/src/engine/graphics3/ShadedGeometrySystem.d.ts.map +1 -1
  110. package/src/engine/graphics3/ShadedGeometrySystem.js +4 -10
  111. package/src/engine/grid/transform2grid/Transform2GridPositionSystem.d.ts +1 -1
  112. package/src/engine/grid/transform2grid/Transform2GridPositionSystem.d.ts.map +1 -1
  113. package/src/engine/grid/transform2grid/Transform2GridPositionSystem.js +4 -8
  114. package/src/engine/intelligence/behavior/ecs/OrbitingBehavior.js +218 -218
  115. package/src/engine/physics/ecs/PhysicsSystem.d.ts.map +1 -1
  116. package/src/engine/physics/ecs/PhysicsSystem.js +2 -5
  117. package/src/engine/sound/ecs/SoundListenerSystem.d.ts.map +1 -1
  118. package/src/engine/sound/ecs/SoundListenerSystem.js +6 -8
  119. package/src/engine/sound/simulation/ecs/AcousticSimulationSystem.d.ts +0 -4
  120. package/src/engine/sound/simulation/ecs/AcousticSimulationSystem.d.ts.map +1 -1
  121. package/src/engine/sound/simulation/ecs/AcousticSimulationSystem.js +5 -11
  122. package/src/shade/playground/particle_editor/README.md +43 -2
  123. package/src/shade/playground/particle_system/particle_prototype.d.ts.map +1 -1
  124. package/src/shade/playground/particle_system/particle_prototype.js +4 -5
  125. package/src/shade/playground/particle_system/particle_scene.js +4 -4
  126. package/src/shade/renderer/Renderer.d.ts.map +1 -1
  127. package/src/shade/renderer/Renderer.js +13 -1
  128. package/src/shade/renderer/particles/DESIGN.md +104 -19
  129. package/src/shade/renderer/particles/GPUParticleSystem.d.ts +64 -50
  130. package/src/shade/renderer/particles/GPUParticleSystem.d.ts.map +1 -1
  131. package/src/shade/renderer/particles/GPUParticleSystem.js +176 -78
  132. package/src/shade/renderer/particles/ParticleConstants.d.ts +59 -23
  133. package/src/shade/renderer/particles/ParticleConstants.d.ts.map +1 -1
  134. package/src/shade/renderer/particles/ParticleConstants.js +66 -23
  135. package/src/shade/renderer/particles/ParticleRenderMode.d.ts +19 -0
  136. package/src/shade/renderer/particles/ParticleRenderMode.d.ts.map +1 -0
  137. package/src/shade/renderer/particles/ParticleRenderMode.js +33 -0
  138. package/src/shade/renderer/particles/data/PARTICLE_EMITTER_STRUCT.d.ts.map +1 -1
  139. package/src/shade/renderer/particles/data/PARTICLE_EMITTER_STRUCT.js +167 -166
  140. package/src/shade/renderer/particles/data/particle_emitter_record.d.ts +37 -0
  141. package/src/shade/renderer/particles/data/particle_emitter_record.d.ts.map +1 -0
  142. package/src/shade/renderer/particles/data/particle_emitter_record.js +109 -0
  143. package/src/shade/renderer/particles/graph/ParticleGroupBuilder.d.ts +3 -3
  144. package/src/shade/renderer/particles/graph/ParticleGroupBuilder.js +303 -303
  145. package/src/shade/renderer/particles/graph/ParticleNodeDescription.d.ts +18 -0
  146. package/src/shade/renderer/particles/graph/ParticleNodeDescription.d.ts.map +1 -1
  147. package/src/shade/renderer/particles/graph/ParticleNodeDescription.js +152 -133
  148. package/src/shade/renderer/particles/graph/ParticleNodeRegistry.d.ts +0 -11
  149. package/src/shade/renderer/particles/graph/ParticleNodeRegistry.d.ts.map +1 -1
  150. package/src/shade/renderer/particles/graph/ParticleNodeRegistry.js +720 -621
  151. package/src/shade/renderer/particles/graph/compile_particle_graph.d.ts.map +1 -1
  152. package/src/shade/renderer/particles/graph/compile_particle_graph.js +8 -3
  153. package/src/shade/renderer/particles/graph/flatten_particle_graph.d.ts.map +1 -1
  154. package/src/shade/renderer/particles/graph/flatten_particle_graph.js +244 -213
  155. package/src/shade/renderer/particles/graph/groups/particle_builtin_groups.d.ts.map +1 -1
  156. package/src/shade/renderer/particles/graph/groups/particle_builtin_groups.js +6 -0
  157. package/src/shade/renderer/particles/graph/groups/particle_groups_geometry.d.ts.map +1 -1
  158. package/src/shade/renderer/particles/graph/groups/particle_groups_geometry.js +294 -319
  159. package/src/shade/renderer/particles/graph/groups/particle_groups_noise.d.ts.map +1 -1
  160. package/src/shade/renderer/particles/graph/groups/particle_groups_noise.js +524 -517
  161. package/src/shade/renderer/particles/graph/groups/particle_groups_psrdnoise.d.ts +22 -0
  162. package/src/shade/renderer/particles/graph/groups/particle_groups_psrdnoise.d.ts.map +1 -0
  163. package/src/shade/renderer/particles/graph/groups/particle_groups_psrdnoise.js +517 -0
  164. package/src/shade/renderer/particles/graph/groups/simplex_reference.d.ts.map +1 -1
  165. package/src/shade/renderer/particles/graph/groups/simplex_reference.js +185 -159
  166. package/src/shade/renderer/particles/graph/make_particle_group.d.ts.map +1 -1
  167. package/src/shade/renderer/particles/graph/make_particle_group.js +2 -20
  168. package/src/shade/renderer/particles/graph/particle_compare_ops.d.ts +17 -0
  169. package/src/shade/renderer/particles/graph/particle_compare_ops.d.ts.map +1 -0
  170. package/src/shade/renderer/particles/graph/particle_compare_ops.js +22 -0
  171. package/src/shade/renderer/particles/graph/particle_graph_diagnostic.d.ts +28 -8
  172. package/src/shade/renderer/particles/graph/particle_graph_diagnostic.d.ts.map +1 -1
  173. package/src/shade/renderer/particles/graph/particle_graph_diagnostic.js +49 -25
  174. package/src/shade/renderer/particles/graph/particle_node_parameters.d.ts +40 -0
  175. package/src/shade/renderer/particles/graph/particle_node_parameters.d.ts.map +1 -1
  176. package/src/shade/renderer/particles/graph/particle_node_parameters.js +312 -234
  177. package/src/shade/renderer/particles/graph/particle_node_presentation.d.ts.map +1 -1
  178. package/src/shade/renderer/particles/graph/particle_node_presentation.js +59 -3
  179. package/src/shade/renderer/particles/graph/particle_ramp.d.ts +230 -0
  180. package/src/shade/renderer/particles/graph/particle_ramp.d.ts.map +1 -0
  181. package/src/shade/renderer/particles/graph/particle_ramp.js +403 -0
  182. package/src/shade/renderer/particles/graph/validate_particle_graph.d.ts +7 -1
  183. package/src/shade/renderer/particles/graph/validate_particle_graph.d.ts.map +1 -1
  184. package/src/shade/renderer/particles/graph/validate_particle_graph.js +66 -10
  185. package/src/shade/renderer/particles/graph_particles.d.ts +23 -69
  186. package/src/shade/renderer/particles/graph_particles.d.ts.map +1 -1
  187. package/src/shade/renderer/particles/graph_particles.js +105 -262
  188. package/src/shade/renderer/particles/graph_particles_avboit.d.ts.map +1 -1
  189. package/src/shade/renderer/particles/graph_particles_avboit.js +3 -2
  190. package/src/shade/renderer/particles/graph_particles_billboard.d.ts +37 -0
  191. package/src/shade/renderer/particles/graph_particles_billboard.d.ts.map +1 -0
  192. package/src/shade/renderer/particles/graph_particles_billboard.js +93 -0
  193. package/src/shade/renderer/particles/isa/InstructionStream.d.ts +51 -22
  194. package/src/shade/renderer/particles/isa/InstructionStream.d.ts.map +1 -1
  195. package/src/shade/renderer/particles/isa/InstructionStream.js +98 -38
  196. package/src/shade/renderer/particles/isa/ParticleProgram.d.ts +49 -8
  197. package/src/shade/renderer/particles/isa/ParticleProgram.d.ts.map +1 -1
  198. package/src/shade/renderer/particles/isa/ParticleProgram.js +95 -150
  199. package/src/shade/renderer/particles/isa/ParticleVMISA.d.ts +306 -64
  200. package/src/shade/renderer/particles/isa/ParticleVMISA.d.ts.map +1 -1
  201. package/src/shade/renderer/particles/isa/ParticleVMISA.js +505 -136
  202. package/src/shade/renderer/particles/isa/particle_assembly.d.ts +36 -12
  203. package/src/shade/renderer/particles/isa/particle_assembly.d.ts.map +1 -1
  204. package/src/shade/renderer/particles/isa/particle_assembly.js +354 -139
  205. package/src/shade/renderer/particles/isa/validate_particle_program.d.ts +9 -0
  206. package/src/shade/renderer/particles/isa/validate_particle_program.d.ts.map +1 -0
  207. package/src/shade/renderer/particles/isa/validate_particle_program.js +128 -0
  208. package/src/shade/renderer/particles/layout/ParticleLayout.d.ts +23 -0
  209. package/src/shade/renderer/particles/layout/ParticleLayout.d.ts.map +1 -1
  210. package/src/shade/renderer/particles/layout/ParticleLayout.js +68 -0
  211. package/src/shade/renderer/particles/optimizer/dag/optimize_particle_program_vector.d.ts +77 -0
  212. package/src/shade/renderer/particles/optimizer/dag/optimize_particle_program_vector.d.ts.map +1 -0
  213. package/src/shade/renderer/particles/optimizer/dag/optimize_particle_program_vector.js +219 -0
  214. package/src/shade/renderer/particles/optimizer/dag/particle_vector_extract.d.ts +83 -0
  215. package/src/shade/renderer/particles/optimizer/dag/particle_vector_extract.d.ts.map +1 -0
  216. package/src/shade/renderer/particles/optimizer/dag/particle_vector_extract.js +283 -0
  217. package/src/shade/renderer/particles/optimizer/dag/particle_vector_graph.d.ts +133 -0
  218. package/src/shade/renderer/particles/optimizer/dag/particle_vector_graph.d.ts.map +1 -0
  219. package/src/shade/renderer/particles/optimizer/dag/particle_vector_graph.js +334 -0
  220. package/src/shade/renderer/particles/optimizer/dag/particle_vector_groups.d.ts +117 -0
  221. package/src/shade/renderer/particles/optimizer/dag/particle_vector_groups.d.ts.map +1 -0
  222. package/src/shade/renderer/particles/optimizer/dag/particle_vector_groups.js +70 -0
  223. package/src/shade/renderer/particles/optimizer/dag/particle_vector_lift.d.ts +97 -0
  224. package/src/shade/renderer/particles/optimizer/dag/particle_vector_lift.d.ts.map +1 -0
  225. package/src/shade/renderer/particles/optimizer/dag/particle_vector_lift.js +462 -0
  226. package/src/shade/renderer/particles/optimizer/dag/particle_vector_lower.d.ts +116 -0
  227. package/src/shade/renderer/particles/optimizer/dag/particle_vector_lower.d.ts.map +1 -0
  228. package/src/shade/renderer/particles/optimizer/dag/particle_vector_lower.js +687 -0
  229. package/src/shade/renderer/particles/optimizer/dag/particle_vector_pack.d.ts +57 -0
  230. package/src/shade/renderer/particles/optimizer/dag/particle_vector_pack.d.ts.map +1 -0
  231. package/src/shade/renderer/particles/optimizer/dag/particle_vector_pack.js +860 -0
  232. package/src/shade/renderer/particles/optimizer/dag/particle_vector_rules.d.ts +115 -0
  233. package/src/shade/renderer/particles/optimizer/dag/particle_vector_rules.d.ts.map +1 -0
  234. package/src/shade/renderer/particles/optimizer/dag/particle_vector_rules.js +264 -0
  235. package/src/shade/renderer/particles/optimizer/optimize_particle_program.d.ts +81 -42
  236. package/src/shade/renderer/particles/optimizer/optimize_particle_program.d.ts.map +1 -1
  237. package/src/shade/renderer/particles/optimizer/optimize_particle_program.js +477 -478
  238. package/src/shade/renderer/particles/optimizer/particle_integer_bounds.d.ts +100 -0
  239. package/src/shade/renderer/particles/optimizer/particle_integer_bounds.d.ts.map +1 -0
  240. package/src/shade/renderer/particles/optimizer/particle_integer_bounds.js +236 -0
  241. package/src/shade/renderer/particles/optimizer/particle_program_analysis.d.ts +139 -59
  242. package/src/shade/renderer/particles/optimizer/particle_program_analysis.d.ts.map +1 -1
  243. package/src/shade/renderer/particles/optimizer/particle_program_analysis.js +764 -647
  244. package/src/shade/renderer/particles/optimizer/particle_program_equivalence.d.ts +40 -7
  245. package/src/shade/renderer/particles/optimizer/particle_program_equivalence.d.ts.map +1 -1
  246. package/src/shade/renderer/particles/optimizer/particle_program_equivalence.js +376 -213
  247. package/src/shade/renderer/particles/optimizer/particle_program_fuzz.d.ts +30 -3
  248. package/src/shade/renderer/particles/optimizer/particle_program_fuzz.d.ts.map +1 -1
  249. package/src/shade/renderer/particles/optimizer/particle_program_fuzz.js +337 -223
  250. package/src/shade/renderer/particles/optimizer/particle_vm_semantics.d.ts +24 -38
  251. package/src/shade/renderer/particles/optimizer/particle_vm_semantics.d.ts.map +1 -1
  252. package/src/shade/renderer/particles/optimizer/particle_vm_semantics.js +166 -206
  253. package/src/shade/renderer/particles/optimizer/pass_allocate_registers.d.ts +99 -0
  254. package/src/shade/renderer/particles/optimizer/pass_allocate_registers.d.ts.map +1 -0
  255. package/src/shade/renderer/particles/optimizer/pass_allocate_registers.js +313 -0
  256. package/src/shade/renderer/particles/optimizer/pass_dedup_constants.d.ts +3 -2
  257. package/src/shade/renderer/particles/optimizer/pass_dedup_constants.d.ts.map +1 -1
  258. package/src/shade/renderer/particles/optimizer/pass_dedup_constants.js +198 -197
  259. package/src/shade/renderer/particles/optimizer/pass_eliminate_dead_code.d.ts.map +1 -1
  260. package/src/shade/renderer/particles/optimizer/pass_fold_constants.d.ts +19 -0
  261. package/src/shade/renderer/particles/optimizer/pass_fold_constants.d.ts.map +1 -1
  262. package/src/shade/renderer/particles/optimizer/pass_fold_constants.js +678 -561
  263. package/src/shade/renderer/particles/optimizer/pass_inline_constants.d.ts +22 -0
  264. package/src/shade/renderer/particles/optimizer/pass_inline_constants.d.ts.map +1 -0
  265. package/src/shade/renderer/particles/optimizer/pass_inline_constants.js +221 -0
  266. package/src/shade/renderer/particles/optimizer/pass_narrow_widths.d.ts.map +1 -1
  267. package/src/shade/renderer/particles/optimizer/pass_narrow_widths.js +63 -72
  268. package/src/shade/renderer/particles/optimizer/pass_pack_registers.d.ts.map +1 -1
  269. package/src/shade/renderer/particles/optimizer/pass_pack_registers.js +87 -86
  270. package/src/shade/renderer/particles/optimizer/pass_peephole.d.ts.map +1 -1
  271. package/src/shade/renderer/particles/optimizer/pass_peephole.js +392 -271
  272. package/src/shade/renderer/particles/optimizer/pass_propagate_copies.d.ts.map +1 -1
  273. package/src/shade/renderer/particles/optimizer/pass_propagate_copies.js +361 -268
  274. package/src/shade/renderer/particles/optimizer/pass_schedule.d.ts +4 -0
  275. package/src/shade/renderer/particles/optimizer/pass_schedule.d.ts.map +1 -0
  276. package/src/shade/renderer/particles/optimizer/pass_schedule.js +244 -0
  277. package/src/shade/renderer/particles/optimizer/pass_share_subexpressions.d.ts.map +1 -1
  278. package/src/shade/renderer/particles/optimizer/pass_share_subexpressions.js +154 -151
  279. package/src/shade/renderer/particles/particle_test_fixtures.d.ts +20 -0
  280. package/src/shade/renderer/particles/particle_test_fixtures.d.ts.map +1 -1
  281. package/src/shade/renderer/particles/particle_test_fixtures.js +229 -203
  282. package/src/shade/renderer/particles/runtime/EmitterRegistry.d.ts +99 -25
  283. package/src/shade/renderer/particles/runtime/EmitterRegistry.d.ts.map +1 -1
  284. package/src/shade/renderer/particles/runtime/EmitterRegistry.js +438 -286
  285. package/src/shade/renderer/particles/runtime/GPUParticleEmitterContext.d.ts +109 -0
  286. package/src/shade/renderer/particles/runtime/GPUParticleEmitterContext.d.ts.map +1 -0
  287. package/src/shade/renderer/particles/runtime/GPUParticleEmitterContext.js +127 -0
  288. package/src/shade/renderer/particles/runtime/ParticleEmitter.d.ts +87 -102
  289. package/src/shade/renderer/particles/runtime/ParticleEmitter.d.ts.map +1 -1
  290. package/src/shade/renderer/particles/runtime/ParticleEmitter.js +184 -163
  291. package/src/shade/renderer/particles/runtime/ProgramArena.d.ts +26 -0
  292. package/src/shade/renderer/particles/runtime/ProgramArena.d.ts.map +1 -1
  293. package/src/shade/renderer/particles/runtime/ProgramHeap.d.ts +130 -0
  294. package/src/shade/renderer/particles/runtime/ProgramHeap.d.ts.map +1 -0
  295. package/src/shade/renderer/particles/runtime/ProgramHeap.js +595 -0
  296. package/src/shade/renderer/particles/runtime/ProgramPlacement.d.ts +91 -0
  297. package/src/shade/renderer/particles/runtime/ProgramPlacement.d.ts.map +1 -0
  298. package/src/shade/renderer/particles/runtime/ProgramPlacement.js +106 -0
  299. package/src/shade/renderer/particles/runtime/create_particle_effect.d.ts +5 -2
  300. package/src/shade/renderer/particles/runtime/create_particle_effect.d.ts.map +1 -1
  301. package/src/shade/renderer/particles/runtime/create_particle_effect.js +22 -19
  302. package/src/shade/renderer/particles/shaders/chunk_particle_emit_step.d.ts +28 -0
  303. package/src/shade/renderer/particles/shaders/chunk_particle_emit_step.d.ts.map +1 -0
  304. package/src/shade/renderer/particles/shaders/chunk_particle_emit_step.js +136 -0
  305. package/src/shade/renderer/particles/shaders/chunk_particle_simulate_step.d.ts +35 -0
  306. package/src/shade/renderer/particles/shaders/chunk_particle_simulate_step.d.ts.map +1 -0
  307. package/src/shade/renderer/particles/shaders/chunk_particle_simulate_step.js +133 -0
  308. package/src/shade/renderer/particles/shaders/shader_particle_emit.d.ts +18 -9
  309. package/src/shade/renderer/particles/shaders/shader_particle_emit.d.ts.map +1 -1
  310. package/src/shade/renderer/particles/shaders/shader_particle_emit.js +20 -80
  311. package/src/shade/renderer/particles/shaders/shader_particle_emit_wide.d.ts +33 -0
  312. package/src/shade/renderer/particles/shaders/shader_particle_emit_wide.d.ts.map +1 -0
  313. package/src/shade/renderer/particles/shaders/shader_particle_emit_wide.js +114 -0
  314. package/src/shade/renderer/particles/shaders/shader_particle_simulate.d.ts +8 -0
  315. package/src/shade/renderer/particles/shaders/shader_particle_simulate.d.ts.map +1 -1
  316. package/src/shade/renderer/particles/shaders/shader_particle_simulate.js +33 -101
  317. package/src/shade/renderer/particles/shaders/shader_particle_simulate_wide.d.ts +45 -0
  318. package/src/shade/renderer/particles/shaders/shader_particle_simulate_wide.d.ts.map +1 -0
  319. package/src/shade/renderer/particles/shaders/shader_particle_simulate_wide.js +123 -0
  320. package/src/shade/renderer/particles/sort/graph_particle_sort.d.ts +51 -0
  321. package/src/shade/renderer/particles/sort/graph_particle_sort.d.ts.map +1 -0
  322. package/src/shade/renderer/particles/sort/graph_particle_sort.js +120 -0
  323. package/src/shade/renderer/particles/vm/PARTICLE_VM_CONTEXT_STRUCT.d.ts +6 -0
  324. package/src/shade/renderer/particles/vm/PARTICLE_VM_CONTEXT_STRUCT.d.ts.map +1 -0
  325. package/src/shade/renderer/particles/vm/PARTICLE_VM_CONTEXT_STRUCT.js +16 -0
  326. package/src/shade/renderer/particles/vm/ParticleVMReference.d.ts +4 -1
  327. package/src/shade/renderer/particles/vm/ParticleVMReference.d.ts.map +1 -1
  328. package/src/shade/renderer/particles/vm/ParticleVMReference.js +261 -140
  329. package/src/shade/renderer/particles/vm/chunk_particle_vm.d.ts.map +1 -1
  330. package/src/shade/renderer/particles/vm/chunk_particle_vm.js +217 -255
  331. package/src/shade/renderer/particles/vm/chunk_particle_vm_registers_fast.d.ts +43 -0
  332. package/src/shade/renderer/particles/vm/chunk_particle_vm_registers_fast.d.ts.map +1 -0
  333. package/src/shade/renderer/particles/vm/chunk_particle_vm_registers_fast.js +55 -0
  334. package/src/shade/renderer/particles/vm/chunk_particle_vm_registers_wide.d.ts +62 -0
  335. package/src/shade/renderer/particles/vm/chunk_particle_vm_registers_wide.d.ts.map +1 -0
  336. package/src/shade/renderer/particles/vm/chunk_particle_vm_registers_wide.js +86 -0
  337. package/src/shade/renderer/shader/chunk/math/chunk_mod_v3f.d.ts +22 -0
  338. package/src/shade/renderer/shader/chunk/math/chunk_mod_v3f.d.ts.map +1 -0
  339. package/src/shade/renderer/shader/chunk/math/chunk_mod_v3f.js +32 -0
  340. package/src/shade/renderer/shader/chunk/math/chunk_mod_v4f.d.ts +9 -0
  341. package/src/shade/renderer/shader/chunk/math/chunk_mod_v4f.d.ts.map +1 -0
  342. package/src/shade/renderer/shader/chunk/math/chunk_mod_v4f.js +19 -0
  343. package/src/shade/renderer/shader/chunk/noise/psrdnoise/README.md +65 -0
  344. package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise2.d.ts +24 -0
  345. package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise2.d.ts.map +1 -0
  346. package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise2.js +83 -0
  347. package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise2_hash.d.ts +25 -0
  348. package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise2_hash.d.ts.map +1 -0
  349. package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise2_hash.js +42 -0
  350. package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise2_kernel.d.ts +31 -0
  351. package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise2_kernel.d.ts.map +1 -0
  352. package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise2_kernel.js +80 -0
  353. package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise3.d.ts +26 -0
  354. package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise3.d.ts.map +1 -0
  355. package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise3.js +109 -0
  356. package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise3_kernel.d.ts +38 -0
  357. package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise3_kernel.d.ts.map +1 -0
  358. package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise3_kernel.js +129 -0
  359. package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise3_permute.d.ts +21 -0
  360. package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise3_permute.d.ts.map +1 -0
  361. package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise3_permute.js +35 -0
  362. package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise3_skew.d.ts +19 -0
  363. package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise3_skew.d.ts.map +1 -0
  364. package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise3_skew.js +28 -0
  365. package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise3_unskew.d.ts +13 -0
  366. package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise3_unskew.d.ts.map +1 -0
  367. package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise3_unskew.js +22 -0
  368. package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_srdnoise2.d.ts +22 -0
  369. package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_srdnoise2.d.ts.map +1 -0
  370. package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_srdnoise2.js +59 -0
  371. package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_srdnoise3.d.ts +23 -0
  372. package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_srdnoise3.d.ts.map +1 -0
  373. package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_srdnoise3.js +74 -0
  374. package/src/shade/wgsl/emulator/ComputeShaderEmulator.d.ts +34 -1
  375. package/src/shade/wgsl/emulator/ComputeShaderEmulator.d.ts.map +1 -1
  376. package/src/shade/wgsl/emulator/ComputeShaderEmulator.js +41 -1
  377. package/src/shade/wgsl/emulator/WGSLJavaScriptCompiler.d.ts +3 -2
  378. package/src/shade/wgsl/emulator/WGSLJavaScriptCompiler.d.ts.map +1 -1
  379. package/src/shade/wgsl/emulator/WGSLJavaScriptCompiler.js +49 -10
  380. package/src/view/minimap/dom/MinimapCameraView.d.ts.map +1 -1
  381. package/src/view/minimap/dom/MinimapCameraView.js +4 -9
  382. package/src/core/model/node-graph/visual/layout/BoxLayoutSpec.d.ts +0 -15
  383. package/src/core/model/node-graph/visual/layout/BoxLayoutSpec.d.ts.map +0 -1
  384. package/src/core/model/node-graph/visual/layout/ConnectedBoxLayouter.d.ts +0 -45
  385. package/src/core/model/node-graph/visual/layout/ConnectedBoxLayouter.d.ts.map +0 -1
  386. package/src/core/model/node-graph/visual/layout/ConnectionEndpointLayoutSpec.d.ts +0 -24
  387. package/src/core/model/node-graph/visual/layout/ConnectionEndpointLayoutSpec.d.ts.map +0 -1
  388. package/src/core/model/node-graph/visual/layout/ConnectionLayoutSpec.d.ts +0 -19
  389. package/src/core/model/node-graph/visual/layout/ConnectionLayoutSpec.d.ts.map +0 -1
  390. package/src/engine/ecs/transform/__probe.d.ts +0 -4
  391. package/src/engine/ecs/transform/__probe.d.ts.map +0 -1
  392. package/src/engine/ecs/util/hideEntityGracefully.d.ts +0 -9
  393. package/src/engine/ecs/util/hideEntityGracefully.d.ts.map +0 -1
  394. package/src/shade/renderer/buffer/table/gpu_database_emulator_image.d.ts +0 -27
  395. package/src/shade/renderer/buffer/table/gpu_database_emulator_image.d.ts.map +0 -1
  396. package/src/shade/renderer/particles/cull/chunk_particle_cull.d.ts +0 -14
  397. package/src/shade/renderer/particles/cull/chunk_particle_cull.d.ts.map +0 -1
  398. package/src/shade/renderer/particles/data/PARTICLE_EMITTER_LAYOUT.d.ts +0 -156
  399. package/src/shade/renderer/particles/data/PARTICLE_EMITTER_LAYOUT.d.ts.map +0 -1
  400. package/src/shade/renderer/particles/runtime/Emitter.d.ts +0 -219
  401. package/src/shade/renderer/particles/runtime/Emitter.d.ts.map +0 -1
  402. package/src/shade/renderer/particles/runtime/ParticleSpawnScheduler.d.ts +0 -77
  403. package/src/shade/renderer/particles/runtime/ParticleSpawnScheduler.d.ts.map +0 -1
  404. package/src/shade/renderer/particles/runtime/ProgramArena.js +0 -155
  405. package/src/shade/renderer/particles/runtime/emitter_database_image.d.ts +0 -15
  406. package/src/shade/renderer/particles/runtime/emitter_database_image.d.ts.map +0 -1
  407. package/src/shade/renderer/particles/runtime/spawn_request_encoding.d.ts +0 -34
  408. package/src/shade/renderer/particles/runtime/spawn_request_encoding.d.ts.map +0 -1
  409. package/src/shade/renderer/particles/shaders/chunk_particle_context.d.ts +0 -32
  410. package/src/shade/renderer/particles/shaders/chunk_particle_context.d.ts.map +0 -1
@@ -1,309 +1,716 @@
1
- import { LAYOUT_SIDE_BOTTOM, LAYOUT_SIDE_LEFT, LAYOUT_SIDE_RIGHT, LAYOUT_SIDE_TOP } from "./LayoutProblem.js";
2
- import { NodeGraphLayoutOptions } from "./NodeGraphLayoutOptions.js";
3
- import { ROUTE_AXIS_H, ROUTE_AXIS_V, RouteSearch } from "./RouteSearch.js";
4
- import { RouteSet } from "./RouteSet.js";
5
-
6
- /**
7
- * Route every wire, letting them argue over the space until they stop.
8
- *
9
- * This is negotiated congestion routing — PathFinder. Every wire is routed as if the lines other
10
- * wires are on merely cost a little extra rather than being forbidden, and then the whole set is
11
- * ripped up and routed again with the price raised. A wire that had somewhere else to go moves; a
12
- * wire with nowhere else stays and the others give way around it. Repeat until nothing is shared.
13
- *
14
- * The reason to do it this way rather than routing wires one at a time and forbidding what is
15
- * already taken is that forbidding makes the *order* decide the drawing. The first wire routed gets
16
- * the straight line through the middle and everything after it detours, whether or not it was the
17
- * one that needed it. Negotiation asks each wire how much it wants the space, by seeing which of
18
- * them finds an alternative when the price goes up, and that question has an answer that does not
19
- * depend on who was asked first.
20
- *
21
- * **History is what makes it settle.** Charging only for what is currently occupied makes two wires
22
- * swap routes every round for ever, each taking the one the other has just left. A line that has
23
- * been fought over stays expensive after it is vacated, so the second time round the pair separates
24
- * instead of trading.
25
- *
26
- * **Only the wires in an argument are asked again.** After the first round, a wire whose route
27
- * shares nothing with anybody has already got what it wanted and is left alone; the rounds after
28
- * the first cost what the remaining conflicts cost rather than what the whole graph costs. Without
29
- * this a graph whose wires nearly all fit spends five further rounds re-deriving the same routes,
30
- * and the work grows as the square of the graph rather than with the trouble in it.
31
- *
32
- * @see McMurchie, Ebeling — *PathFinder: A Negotiation-Based Performance-Driven Router for FPGAs*
33
- * (1995)
34
- *
35
- * @param {LayoutProblem} problem placement must be finished
36
- * @param {RouteGrid} grid
37
- * @param {NodeGraphLayoutOptions} options
38
- * @returns {RouteSet}
39
- *
40
- * @author Alex Goldring
41
- * @copyright Company Named Limited (c) 2026
42
- */
43
- export function route_connections(problem, grid, options) {
44
- const count = problem.connection_count;
45
-
46
- const routes = new RouteSet(count);
47
-
48
- if (count === 0) {
49
- return routes;
50
- }
51
-
52
- const search = new RouteSearch(grid);
53
-
54
- // present cost rises between rounds, and the caller's options are not ours to change
55
- const working = NodeGraphLayoutOptions.from(options);
56
-
57
- const start_vertex = new Int32Array(count);
58
- const start_axis = new Uint8Array(count);
59
- const goal_vertex = new Int32Array(count);
60
- const goal_axis = new Uint8Array(count);
61
-
62
- const port_x = new Float64Array(count * 2);
63
- const port_y = new Float64Array(count * 2);
64
-
65
- const escape_x = new Float64Array(count * 2);
66
- const escape_y = new Float64Array(count * 2);
67
-
68
- const terminal = { port_x, port_y, escape_x, escape_y };
69
-
70
- for (let i = 0; i < count; i++) {
71
- write_terminal(problem, grid, options, i, true, start_vertex, start_axis, terminal);
72
- write_terminal(problem, grid, options, i, false, goal_vertex, goal_axis, terminal);
73
- }
74
-
75
- // Grid vertices of each route as it currently stands, so that a wire can be taken off the grid
76
- // before being routed again. Corner points alone are not enough for that — what has to be given
77
- // back is every line the wire occupies.
78
- const held = [];
79
-
80
- for (let i = 0; i < count; i++) {
81
- held.push(null);
82
- }
83
-
84
- // Which wires are worth asking again. Everything, to begin with, because nothing is routed yet.
85
- const contested = new Uint8Array(count).fill(1);
86
-
87
- for (let round = 0; round < options.congestion_iterations; round++) {
88
- for (let i = 0; i < count; i++) {
89
- if (contested[i] === 0) {
90
- continue;
91
- }
92
-
93
- if (held[i] !== null) {
94
- occupy(grid, held[i], -1);
95
- }
96
-
97
- const found = search.find(start_vertex[i], start_axis[i], goal_vertex[i], goal_axis[i], working);
98
-
99
- if (!found) {
100
- held[i] = null;
101
-
102
- continue;
103
- }
104
-
105
- const path = search.path.slice(0, search.path_length);
106
-
107
- held[i] = path;
108
-
109
- occupy(grid, path, 1);
110
- }
111
-
112
- if (mark_contested(grid, held, contested) === 0) {
113
- break;
114
- }
115
-
116
- remember(grid, options.history_rate);
117
-
118
- working.present_cost *= options.present_growth;
119
- }
120
-
121
- for (let i = 0; i < count; i++) {
122
- routes.begin(i);
123
-
124
- routes.push(port_x[i * 2], port_y[i * 2]);
125
-
126
- const path = held[i];
127
-
128
- if (path !== null) {
129
- for (let k = 0; k < path.length; k++) {
130
- routes.push(grid.xs[path[k] % grid.nx], grid.ys[(path[k] / grid.nx) | 0]);
131
- }
132
- } else {
133
- // Nothing was found — the wire is boxed in, or the search ran out of room to look. It
134
- // still has to be drawn, and going out of one port and into the other the way every
135
- // other wire does is a better failure than a line through the middle of a node.
136
- routes.push(escape_x[i * 2], escape_y[i * 2]);
137
- routes.push(escape_x[i * 2 + 1], escape_y[i * 2 + 1]);
138
- }
139
-
140
- routes.push(port_x[i * 2 + 1], port_y[i * 2 + 1]);
141
-
142
- routes.end(i);
143
- }
144
-
145
- return routes;
146
- }
147
-
148
- /**
149
- * Work out where one end of a wire meets the grid.
150
- *
151
- * A wire does not start at its port, it starts a clearance away from the box on the side the port
152
- * is on — the piece between the two is a straight stub, which is what makes it obvious which port
153
- * the wire belongs to. That stub is also where the wire's direction comes from: a port on the right
154
- * of a box is left horizontally, whatever the wire does afterwards.
155
- *
156
- * @param {LayoutProblem} problem
157
- * @param {RouteGrid} grid
158
- * @param {NodeGraphLayoutOptions} options
159
- * @param {number} index connection
160
- * @param {boolean} leaving
161
- * @param {Int32Array} vertex written
162
- * @param {Uint8Array} axis written
163
- * @param {{port_x: Float64Array, port_y: Float64Array, escape_x: Float64Array, escape_y: Float64Array}} terminal
164
- * written, two entries per connection
165
- * @returns {void}
166
- */
167
- function write_terminal(problem, grid, options, index, leaving, vertex, axis, terminal) {
168
- const boxes = problem.boxes;
169
-
170
- const box = leaving ? problem.from_box[index] : problem.to_box[index];
171
- const side = leaving ? problem.from_side[index] : problem.to_side[index];
172
-
173
- const x = boxes.box_x[box] + (leaving ? problem.from_dx[index] : problem.to_dx[index]);
174
- const y = boxes.box_y[box] + (leaving ? problem.from_dy[index] : problem.to_dy[index]);
175
-
176
- const at = index * 2 + (leaving ? 0 : 1);
177
-
178
- terminal.port_x[at] = x;
179
- terminal.port_y[at] = y;
180
-
181
- const clearance = options.clearance;
182
-
183
- let escape_x = x;
184
- let escape_y = y;
185
-
186
- switch (side) {
187
- case LAYOUT_SIDE_LEFT:
188
- escape_x = boxes.box_x[box] - clearance;
189
- break;
190
- case LAYOUT_SIDE_RIGHT:
191
- escape_x = boxes.box_x[box] + boxes.box_w[box] + clearance;
192
- break;
193
- case LAYOUT_SIDE_TOP:
194
- escape_y = boxes.box_y[box] - clearance;
195
- break;
196
- case LAYOUT_SIDE_BOTTOM:
197
- escape_y = boxes.box_y[box] + boxes.box_h[box] + clearance;
198
- break;
199
- }
200
-
201
- terminal.escape_x[at] = escape_x;
202
- terminal.escape_y[at] = escape_y;
203
-
204
- vertex[index] = grid.nearest_x(escape_x) + grid.nearest_y(escape_y) * grid.nx;
205
-
206
- axis[index] = side === LAYOUT_SIDE_TOP || side === LAYOUT_SIDE_BOTTOM ? ROUTE_AXIS_V : ROUTE_AXIS_H;
207
- }
208
-
209
- /**
210
- * Claim, or give back, every line a route runs along.
211
- *
212
- * Only what is on the grid is claimed. The stub between a port and the grid is not on it and is
213
- * never charged, which is what lets several wires leave one port: they share that stub whatever it
214
- * would cost, having no say in where their own port is, and they are only asked to separate once
215
- * they are somewhere they could have chosen.
216
- *
217
- * @param {RouteGrid} grid
218
- * @param {Int32Array} path grid vertices
219
- * @param {number} delta 1 to claim, -1 to give back
220
- * @returns {void}
221
- */
222
- function occupy(grid, path, delta) {
223
- const nx = grid.nx;
224
-
225
- for (let i = 0; i + 1 < path.length; i++) {
226
- const a = path[i];
227
- const b = path[i + 1];
228
-
229
- if (b === a + 1) {
230
- grid.use_h[a] += delta;
231
- } else if (a === b + 1) {
232
- grid.use_h[b] += delta;
233
- } else if (b === a + nx) {
234
- grid.use_v[a] += delta;
235
- } else {
236
- grid.use_v[b] += delta;
237
- }
238
- }
239
- }
240
-
241
- /**
242
- * Say which wires are still sharing a line with somebody, and how many wires that is.
243
- *
244
- * A wire is asked to look again only when some part of its own route is contested. One that is
245
- * clear of everybody has already found what it wanted, and re-deriving it costs a full search to
246
- * arrive back where it started.
247
- *
248
- * @param {RouteGrid} grid
249
- * @param {(Int32Array|null)[]} held
250
- * @param {Uint8Array} contested written
251
- * @returns {number} wires still sharing a line
252
- */
253
- function mark_contested(grid, held, contested) {
254
- const nx = grid.nx;
255
-
256
- let total = 0;
257
-
258
- for (let i = 0; i < held.length; i++) {
259
- const path = held[i];
260
-
261
- if (path === null) {
262
- // No route was found for it. Congestion is not what it is waiting for, but the grid it
263
- // failed on is not the grid the next round presents, so it gets asked again rather than
264
- // being written off on the strength of one attempt.
265
- contested[i] = 1;
266
-
267
- continue;
268
- }
269
-
270
- contested[i] = 0;
271
-
272
- for (let k = 0; k + 1 < path.length; k++) {
273
- const a = path[k];
274
- const b = path[k + 1];
275
-
276
- const shared = b === a + 1 ? grid.use_h[a] > 1
277
- : a === b + 1 ? grid.use_h[b] > 1
278
- : b === a + nx ? grid.use_v[a] > 1
279
- : grid.use_v[b] > 1;
280
-
281
- if (shared) {
282
- contested[i] = 1;
283
-
284
- total++;
285
-
286
- break;
287
- }
288
- }
289
- }
290
-
291
- return total;
292
- }
293
-
294
- /**
295
- * @param {RouteGrid} grid
296
- * @param {number} rate
297
- * @returns {void}
298
- */
299
- function remember(grid, rate) {
300
- for (let i = 0; i < grid.vertex_count; i++) {
301
- if (grid.use_h[i] > 1) {
302
- grid.history_h[i] += rate * (grid.use_h[i] - 1);
303
- }
304
-
305
- if (grid.use_v[i] > 1) {
306
- grid.history_v[i] += rate * (grid.use_v[i] - 1);
307
- }
308
- }
309
- }
1
+ import { LAYOUT_SIDE_BOTTOM, LAYOUT_SIDE_LEFT, LAYOUT_SIDE_RIGHT, LAYOUT_SIDE_TOP } from "./LayoutProblem.js";
2
+ import { NodeGraphLayoutOptions } from "./NodeGraphLayoutOptions.js";
3
+ import { ROUTE_AXIS_H, ROUTE_AXIS_V, RouteSearch } from "./RouteSearch.js";
4
+ import { RouteSet } from "./RouteSet.js";
5
+
6
+ /**
7
+ * Route every wire, letting them argue over the space until they stop.
8
+ *
9
+ * This is negotiated congestion routing — PathFinder. Every wire is routed as if the lines other
10
+ * wires are on merely cost a little extra rather than being forbidden, and then the whole set is
11
+ * ripped up and routed again with the price raised. A wire that had somewhere else to go moves; a
12
+ * wire with nowhere else stays and the others give way around it. Repeat until nothing is shared.
13
+ *
14
+ * The reason to do it this way rather than routing wires one at a time and forbidding what is
15
+ * already taken is that forbidding makes the *order* decide the drawing. The first wire routed gets
16
+ * the straight line through the middle and everything after it detours, whether or not it was the
17
+ * one that needed it. Negotiation asks each wire how much it wants the space, by seeing which of
18
+ * them finds an alternative when the price goes up, and that question has an answer that does not
19
+ * depend on who was asked first.
20
+ *
21
+ * **History is what makes it settle.** Charging only for what is currently occupied makes two wires
22
+ * swap routes every round for ever, each taking the one the other has just left. A line that has
23
+ * been fought over stays expensive after it is vacated, so the second time round the pair separates
24
+ * instead of trading.
25
+ *
26
+ * **Only the wires in an argument are asked again.** After the first round, a wire whose route
27
+ * shares nothing with anybody has already got what it wanted and is left alone; the rounds after
28
+ * the first cost what the remaining conflicts cost rather than what the whole graph costs. Without
29
+ * this a graph whose wires nearly all fit spends five further rounds re-deriving the same routes,
30
+ * and the work grows as the square of the graph rather than with the trouble in it.
31
+ *
32
+ * **The wires leaving one port are one net, and share lines with each other for nothing.** They
33
+ * are one signal, and they leave the port along one stub whatever anybody does; charging them for
34
+ * running along the same line after it prices up the one thing they cannot help, every round,
35
+ * until they are pushed onto whichever line is left and pulled apart there in an order nobody
36
+ * chose — which is how a fan of six wires came to be drawn crossing itself. Left to share, they
37
+ * run out of the port as one line and fork where each of them leaves, which is a bus, and is what
38
+ * a fan of wires from one port looks like in any drawing done by hand. See
39
+ * {@link RouteGrid#owner_h}.
40
+ *
41
+ * **A long wire is routed through its lanes.** The placement gave every wire that crosses a column
42
+ * a lane in it — a point the ordering stage put where it crosses the fewest other wires and the
43
+ * coordinate stage lined up with the lanes either side of it — and a wire with lanes is routed as
44
+ * a chain of short searches, one per lane, each ending where the lane is and going through it
45
+ * horizontally. That is the placement's crossing-minimised order drawn, rather than left for the
46
+ * router to rediscover one wire at a time; and it is what makes a wire forty columns long cheap to
47
+ * route, where one search across the whole drawing floods the grid and gives up. A wire whose
48
+ * lanes cannot be reached is routed free, as a wire with no lanes always is.
49
+ *
50
+ * **Crossings are not an argument, and are settled afterwards.** Sharing a line is something the
51
+ * negotiation can price up until one wire leaves; crossing is allowed, merely costed, and a wire
52
+ * that crosses six others may have had no way not to. But the order the wires are first routed in
53
+ * hides most of the crossings from the wires that make them: a wire routed early sees an empty
54
+ * grid, takes whichever of its equal routes surfaces first, and is then crossed by everything
55
+ * routed after it — and since it shares nothing it is never asked again. So once nothing is shared,
56
+ * every wire something has been done to is asked once more, with all the others present, and moves
57
+ * only if the route it finds is cheaper than the one it has. That is a descent on the total cost
58
+ * of the drawing — a crossing costs both wires the same, so a wire making itself cheaper makes the
59
+ * whole cheaper — and it stops on its own when a round moves nothing.
60
+ *
61
+ * @see McMurchie, Ebeling — *PathFinder: A Negotiation-Based Performance-Driven Router for FPGAs*
62
+ * (1995)
63
+ *
64
+ * @param {LayoutProblem} problem placement must be finished
65
+ * @param {RouteGrid} grid
66
+ * @param {NodeGraphLayoutOptions} options
67
+ * @returns {RouteSet}
68
+ *
69
+ * @author Alex Goldring
70
+ * @copyright Company Named Limited (c) 2026
71
+ */
72
+ export function route_connections(problem, grid, options) {
73
+ const count = problem.connection_count;
74
+
75
+ const routes = new RouteSet(count);
76
+
77
+ if (count === 0) {
78
+ return routes;
79
+ }
80
+
81
+ const search = new RouteSearch(grid);
82
+
83
+ // present cost rises between rounds, and the caller's options are not ours to change
84
+ const working = NodeGraphLayoutOptions.from(options);
85
+
86
+ const port_x = new Float64Array(count * 2);
87
+ const port_y = new Float64Array(count * 2);
88
+
89
+ const escape_x = new Float64Array(count * 2);
90
+ const escape_y = new Float64Array(count * 2);
91
+
92
+ const wires = new WireSet(problem, grid, options, { port_x, port_y, escape_x, escape_y });
93
+
94
+ routes.net = wires.net;
95
+
96
+ // Routes as they currently stand, so that a wire can be taken off the grid before being routed
97
+ // again. Corner points alone are not enough for that — what has to be given back is every
98
+ // line the wire occupies.
99
+ /** @type {(WireRoute|null)[]} */
100
+ const held = [];
101
+
102
+ for (let i = 0; i < count; i++) {
103
+ held.push(null);
104
+ }
105
+
106
+ // Which wires are worth asking again. Everything, to begin with, because nothing is routed yet.
107
+ const contested = new Uint8Array(count).fill(1);
108
+
109
+ for (let round = 0; round < options.congestion_iterations; round++) {
110
+ for (let i = 0; i < count; i++) {
111
+ if (contested[i] === 0) {
112
+ continue;
113
+ }
114
+
115
+ if (held[i] !== null) {
116
+ wires.occupy(i, held[i], -1);
117
+ }
118
+
119
+ held[i] = wires.route(search, i, working);
120
+
121
+ if (held[i] !== null) {
122
+ wires.occupy(i, held[i], 1);
123
+ }
124
+ }
125
+
126
+ if (mark_contested(grid, held, wires.net, contested) === 0) {
127
+ break;
128
+ }
129
+
130
+ remember(grid, options.history_rate);
131
+
132
+ working.present_cost *= options.present_growth;
133
+ }
134
+
135
+ // Sharing is priced at what the negotiation would have charged had it run out of rounds: an
136
+ // improvement is not allowed to buy a route with a line the negotiation had already priced a
137
+ // wire off, and the wires still sharing something pay the same price for it as everyone else.
138
+ working.present_cost = options.present_cost * options.present_growth ** options.congestion_iterations;
139
+
140
+ for (let round = 0; round < options.improvement_rounds; round++) {
141
+ if (improve(search, wires, held, working) === 0) {
142
+ break;
143
+ }
144
+ }
145
+
146
+ for (let i = 0; i < count; i++) {
147
+ routes.begin(i);
148
+
149
+ routes.push(port_x[i * 2], port_y[i * 2]);
150
+
151
+ const route = held[i];
152
+
153
+ if (route !== null) {
154
+ const path = route.path;
155
+
156
+ for (let k = 0; k < path.length; k++) {
157
+ routes.push(grid.xs[path[k] % grid.nx], grid.ys[(path[k] / grid.nx) | 0]);
158
+ }
159
+ } else {
160
+ // Nothing was found — the wire is boxed in, or the search ran out of room to look. It
161
+ // still has to be drawn, and going out of one port and into the other the way every
162
+ // other wire does is a better failure than a line through the middle of a node.
163
+ routes.push(escape_x[i * 2], escape_y[i * 2]);
164
+ routes.push(escape_x[i * 2 + 1], escape_y[i * 2 + 1]);
165
+ }
166
+
167
+ routes.push(port_x[i * 2 + 1], port_y[i * 2 + 1]);
168
+
169
+ routes.end(i);
170
+ }
171
+
172
+ return routes;
173
+ }
174
+
175
+ /**
176
+ * One wire's route: its vertices, where each leg of it begins, and what it cost when it was chosen.
177
+ *
178
+ * A leg is one search — from the start to the first lane, from lane to lane, from the last lane to
179
+ * the goal — and the legs share their end points, so `leg[k]` is the index into `path` of the
180
+ * vertex leg `k` begins at, which is the vertex leg `k - 1` ended at. A wire with no lanes is one
181
+ * leg.
182
+ */
183
+ class WireRoute {
184
+
185
+ /**
186
+ * @type {Int32Array}
187
+ */
188
+ path;
189
+
190
+ /**
191
+ * Length `legs + 1`; the last entry is `path.length - 1`.
192
+ * @type {Int32Array}
193
+ */
194
+ leg;
195
+
196
+ /**
197
+ * What each wire believed its route cost when it chose it. A wire whose route has since come
198
+ * to cost more has had something done to it — crossed, or joined on a line — and is the one
199
+ * worth asking whether it would now rather be elsewhere.
200
+ * @type {number}
201
+ */
202
+ cost;
203
+
204
+ /**
205
+ * @param {Int32Array} path
206
+ * @param {Int32Array} leg
207
+ * @param {number} cost
208
+ */
209
+ constructor(path, leg, cost) {
210
+ this.path = path;
211
+ this.leg = leg;
212
+ this.cost = cost;
213
+ }
214
+ }
215
+
216
+ /**
217
+ * Every wire's terminals and waypoints, on the grid, and the two things done with them: finding a
218
+ * wire a route, and putting one on the grid or taking it off.
219
+ */
220
+ class WireSet {
221
+
222
+ /**
223
+ * @type {RouteGrid}
224
+ */
225
+ grid;
226
+
227
+ /**
228
+ * @type {Int32Array}
229
+ */
230
+ start_vertex;
231
+
232
+ /**
233
+ * @type {Uint8Array}
234
+ */
235
+ start_axis;
236
+
237
+ /**
238
+ * @type {Int32Array}
239
+ */
240
+ goal_vertex;
241
+
242
+ /**
243
+ * @type {Uint8Array}
244
+ */
245
+ goal_axis;
246
+
247
+ /**
248
+ * Net of each wire: the wires leaving one port share a number. See {@link RouteGrid#owner_h}.
249
+ * @type {Int32Array}
250
+ */
251
+ net;
252
+
253
+ /**
254
+ * Where each wire's waypoints begin in {@link waypoint}. Length `count + 1`.
255
+ * @type {Int32Array}
256
+ */
257
+ waypoint_start;
258
+
259
+ /**
260
+ * Grid vertex of every lane, grouped by wire in the order the wire goes through them.
261
+ * @type {Int32Array}
262
+ */
263
+ waypoint;
264
+
265
+ /**
266
+ * @param {LayoutProblem} problem
267
+ * @param {RouteGrid} grid
268
+ * @param {NodeGraphLayoutOptions} options
269
+ * @param {{port_x: Float64Array, port_y: Float64Array, escape_x: Float64Array, escape_y: Float64Array}} terminal
270
+ * written, two entries per connection
271
+ */
272
+ constructor(problem, grid, options, terminal) {
273
+ const count = problem.connection_count;
274
+
275
+ this.grid = grid;
276
+
277
+ this.start_vertex = new Int32Array(count);
278
+ this.start_axis = new Uint8Array(count);
279
+ this.goal_vertex = new Int32Array(count);
280
+ this.goal_axis = new Uint8Array(count);
281
+
282
+ for (let i = 0; i < count; i++) {
283
+ write_terminal(problem, grid, options, i, true, this.start_vertex, this.start_axis, terminal);
284
+ write_terminal(problem, grid, options, i, false, this.goal_vertex, this.goal_axis, terminal);
285
+ }
286
+
287
+ // one net per port wires leave from, numbered in the order the ports are first met
288
+ this.net = new Int32Array(count);
289
+
290
+ const net_of_port = new Map();
291
+
292
+ for (let i = 0; i < count; i++) {
293
+ const key = `${problem.from_box[i]}:${problem.from_dx[i]}:${problem.from_dy[i]}`;
294
+
295
+ let net = net_of_port.get(key);
296
+
297
+ if (net === undefined) {
298
+ net = net_of_port.size;
299
+
300
+ net_of_port.set(key, net);
301
+ }
302
+
303
+ this.net[i] = net;
304
+ }
305
+
306
+ // a problem built by hand, or before placement, has no lanes at all
307
+ const lane_box = problem.lane_box ?? new Int32Array(0);
308
+
309
+ this.waypoint_start = problem.lane_start ?? new Int32Array(count + 1);
310
+
311
+ const lane_count = lane_box.length;
312
+
313
+ this.waypoint = new Int32Array(lane_count);
314
+
315
+ const boxes = problem.boxes;
316
+
317
+ for (let k = 0; k < lane_count; k++) {
318
+ const lane = lane_box[k];
319
+
320
+ this.waypoint[k] = grid.nearest_x(boxes.box_x[lane]) + grid.nearest_y(boxes.box_y[lane]) * grid.nx;
321
+ }
322
+ }
323
+
324
+ /**
325
+ * Find a wire its route: through its lanes if it has any and they can be reached, free
326
+ * otherwise.
327
+ *
328
+ * @param {RouteSearch} search
329
+ * @param {number} index wire
330
+ * @param {NodeGraphLayoutOptions} options
331
+ * @returns {WireRoute|null} null when no route was found
332
+ */
333
+ route(search, index, options) {
334
+ const first = this.waypoint_start[index];
335
+ const last = this.waypoint_start[index + 1];
336
+
337
+ if (last > first) {
338
+ const through = this.__route_legs(search, index, options, first, last);
339
+
340
+ if (through !== null) {
341
+ return through;
342
+ }
343
+ }
344
+
345
+ return this.__route_legs(search, index, options, 0, 0);
346
+ }
347
+
348
+ /**
349
+ * What a route costs under the grid as it stands, leg by leg. See {@link RouteSearch#cost_of}.
350
+ *
351
+ * @param {RouteSearch} search
352
+ * @param {number} index wire
353
+ * @param {WireRoute} route its own occupancy must be off the grid
354
+ * @param {NodeGraphLayoutOptions} options
355
+ * @returns {number}
356
+ */
357
+ cost_of(search, index, route, options) {
358
+ const path = route.path;
359
+ const leg = route.leg;
360
+
361
+ const legs = leg.length - 1;
362
+
363
+ let cost = 0;
364
+
365
+ for (let k = 0; k < legs; k++) {
366
+ const from = leg[k];
367
+ const to = leg[k + 1];
368
+
369
+ cost += search.cost_of(
370
+ path.subarray(from, to + 1),
371
+ to + 1 - from,
372
+ k === 0 ? this.start_axis[index] : ROUTE_AXIS_H,
373
+ k === legs - 1 ? this.goal_axis[index] : ROUTE_AXIS_H,
374
+ options,
375
+ k === legs - 1,
376
+ this.net[index]
377
+ );
378
+ }
379
+
380
+ return cost;
381
+ }
382
+
383
+ /**
384
+ * Claim, or give back, every line a route runs along, and every vertex it goes straight
385
+ * through.
386
+ *
387
+ * Only what is on the grid is claimed. The stub between a port and the grid is not on it and is
388
+ * never charged, which is what lets several wires leave one port: they share that stub whatever
389
+ * it would cost, having no say in where their own port is, and they are only asked to separate
390
+ * once they are somewhere they could have chosen. The stub does say which way the wire arrives
391
+ * at the grid, though, and that is what decides whether it goes straight through the vertex
392
+ * where it meets it — see {@link RouteGrid#through_h}.
393
+ *
394
+ * @param {number} index wire
395
+ * @param {WireRoute} route
396
+ * @param {number} delta 1 to claim, -1 to give back
397
+ * @returns {void}
398
+ */
399
+ occupy(index, route, delta) {
400
+ const grid = this.grid;
401
+
402
+ const nx = grid.nx;
403
+
404
+ const path = route.path;
405
+
406
+ const last = path.length - 1;
407
+
408
+ const net = this.net[index];
409
+
410
+ // the axis the route is on as it arrives at each vertex, beginning with the stub's
411
+ let arriving = this.start_axis[index];
412
+
413
+ for (let i = 0; i <= last; i++) {
414
+ const a = path[i];
415
+
416
+ let leaving;
417
+
418
+ if (i === last) {
419
+ leaving = this.goal_axis[index];
420
+ } else {
421
+ const b = path[i + 1];
422
+
423
+ if (b === a + 1) {
424
+ leaving = ROUTE_AXIS_H;
425
+ grid.claim_h(a, net, delta);
426
+ } else if (a === b + 1) {
427
+ leaving = ROUTE_AXIS_H;
428
+ grid.claim_h(b, net, delta);
429
+ } else if (b === a + nx) {
430
+ leaving = ROUTE_AXIS_V;
431
+ grid.claim_v(a, net, delta);
432
+ } else {
433
+ leaving = ROUTE_AXIS_V;
434
+ grid.claim_v(b, net, delta);
435
+ }
436
+ }
437
+
438
+ if (leaving === arriving) {
439
+ if (leaving === ROUTE_AXIS_H) {
440
+ grid.through_h[a] += delta;
441
+ } else {
442
+ grid.through_v[a] += delta;
443
+ }
444
+ }
445
+
446
+ arriving = leaving;
447
+ }
448
+ }
449
+
450
+ /**
451
+ * Route a wire as a chain of searches through the given waypoints, of which there may be none.
452
+ *
453
+ * @param {RouteSearch} search
454
+ * @param {number} index wire
455
+ * @param {NodeGraphLayoutOptions} options
456
+ * @param {number} first index into {@link waypoint}
457
+ * @param {number} last exclusive
458
+ * @returns {WireRoute|null}
459
+ * @private
460
+ */
461
+ __route_legs(search, index, options, first, last) {
462
+ const legs = last - first + 1;
463
+
464
+ const leg = new Int32Array(legs + 1);
465
+
466
+ let path = null;
467
+ let length = 0;
468
+ let cost = 0;
469
+
470
+ let at = this.start_vertex[index];
471
+ let axis = this.start_axis[index];
472
+
473
+ for (let k = 0; k < legs; k++) {
474
+ const final = k === legs - 1;
475
+
476
+ const goal = final ? this.goal_vertex[index] : this.waypoint[first + k];
477
+ const goal_axis = final ? this.goal_axis[index] : ROUTE_AXIS_H;
478
+
479
+ if (!search.find(at, axis, goal, goal_axis, options, final, this.net[index])) {
480
+ return null;
481
+ }
482
+
483
+ const found = search.path;
484
+ const found_length = search.path_length;
485
+
486
+ if (path === null) {
487
+ path = found.slice(0, found_length);
488
+ length = found_length;
489
+ } else {
490
+ // the leg begins where the last one ended, and that vertex is already in
491
+ const grown = new Int32Array(length + found_length - 1);
492
+
493
+ grown.set(path.subarray(0, length));
494
+ grown.set(found.subarray(1, found_length), length);
495
+
496
+ path = grown;
497
+ length = grown.length;
498
+ }
499
+
500
+ leg[k + 1] = length - 1;
501
+
502
+ cost += search.cost;
503
+
504
+ at = goal;
505
+ axis = ROUTE_AXIS_H;
506
+ }
507
+
508
+ return new WireRoute(path, leg, cost);
509
+ }
510
+ }
511
+
512
+ /**
513
+ * Work out where one end of a wire meets the grid.
514
+ *
515
+ * A wire does not start at its port, it starts a clearance away from the box on the side the port
516
+ * is on — the piece between the two is a straight stub, which is what makes it obvious which port
517
+ * the wire belongs to. That stub is also where the wire's direction comes from: a port on the right
518
+ * of a box is left horizontally, whatever the wire does afterwards.
519
+ *
520
+ * @param {LayoutProblem} problem
521
+ * @param {RouteGrid} grid
522
+ * @param {NodeGraphLayoutOptions} options
523
+ * @param {number} index connection
524
+ * @param {boolean} leaving
525
+ * @param {Int32Array} vertex written
526
+ * @param {Uint8Array} axis written
527
+ * @param {{port_x: Float64Array, port_y: Float64Array, escape_x: Float64Array, escape_y: Float64Array}} terminal
528
+ * written, two entries per connection
529
+ * @returns {void}
530
+ */
531
+ function write_terminal(problem, grid, options, index, leaving, vertex, axis, terminal) {
532
+ const boxes = problem.boxes;
533
+
534
+ const box = leaving ? problem.from_box[index] : problem.to_box[index];
535
+ const side = leaving ? problem.from_side[index] : problem.to_side[index];
536
+
537
+ const x = boxes.box_x[box] + (leaving ? problem.from_dx[index] : problem.to_dx[index]);
538
+ const y = boxes.box_y[box] + (leaving ? problem.from_dy[index] : problem.to_dy[index]);
539
+
540
+ const at = index * 2 + (leaving ? 0 : 1);
541
+
542
+ terminal.port_x[at] = x;
543
+ terminal.port_y[at] = y;
544
+
545
+ const clearance = options.clearance;
546
+
547
+ let escape_x = x;
548
+ let escape_y = y;
549
+
550
+ switch (side) {
551
+ case LAYOUT_SIDE_LEFT:
552
+ escape_x = boxes.box_x[box] - clearance;
553
+ break;
554
+ case LAYOUT_SIDE_RIGHT:
555
+ escape_x = boxes.box_x[box] + boxes.box_w[box] + clearance;
556
+ break;
557
+ case LAYOUT_SIDE_TOP:
558
+ escape_y = boxes.box_y[box] - clearance;
559
+ break;
560
+ case LAYOUT_SIDE_BOTTOM:
561
+ escape_y = boxes.box_y[box] + boxes.box_h[box] + clearance;
562
+ break;
563
+ }
564
+
565
+ terminal.escape_x[at] = escape_x;
566
+ terminal.escape_y[at] = escape_y;
567
+
568
+ vertex[index] = grid.nearest_x(escape_x) + grid.nearest_y(escape_y) * grid.nx;
569
+
570
+ axis[index] = side === LAYOUT_SIDE_TOP || side === LAYOUT_SIDE_BOTTOM ? ROUTE_AXIS_V : ROUTE_AXIS_H;
571
+ }
572
+
573
+ /**
574
+ * One round of asking every wire whether it would rather be somewhere else, now that everything
575
+ * else is routed.
576
+ *
577
+ * Each wire is taken off the grid, asked for its cheapest route through what is left, and put back
578
+ * — on the new route if that is cheaper than the old one under the same grid, on the old one
579
+ * otherwise. The search is exact, so the new route is never dearer; it is refused when it is merely
580
+ * no cheaper, which is what keeps the round from moving wires between routes that tie for ever.
581
+ *
582
+ * Only the wires something has happened to are asked. A wire's route costs what it did when the
583
+ * wire chose it until some other wire comes to cross it or to share a line with it, and a wire
584
+ * whose route still costs what it believed has nothing new to consider: it already took the best
585
+ * route through the grid it was shown, and that grid has not changed along it. What this misses is
586
+ * a wire whose surroundings became *cheaper* — a neighbour that moved away — and the round after a
587
+ * neighbour moves is not the place to chase that; a search is the size of the grid and a wire it
588
+ * would be spent on is one that was already content. Asking everyone once was tried, and moved
589
+ * nothing worth the searches.
590
+ *
591
+ * A wire that has no route at all is asked again too: the grid it failed on is not the one it is
592
+ * being offered now.
593
+ *
594
+ * @param {RouteSearch} search
595
+ * @param {WireSet} wires
596
+ * @param {(WireRoute|null)[]} held current routes, mutated where a wire moves
597
+ * @param {NodeGraphLayoutOptions} options with the present cost to price sharing at
598
+ * @returns {number} how many wires moved
599
+ */
600
+ function improve(search, wires, held, options) {
601
+ let moved = 0;
602
+
603
+ for (let i = 0; i < held.length; i++) {
604
+ const old = held[i];
605
+
606
+ let old_cost = Infinity;
607
+
608
+ if (old !== null) {
609
+ wires.occupy(i, old, -1);
610
+
611
+ old_cost = wires.cost_of(search, i, old, options);
612
+
613
+ if (old_cost <= old.cost) {
614
+ // nothing has been done to it since it chose this route
615
+ wires.occupy(i, old, 1);
616
+
617
+ continue;
618
+ }
619
+ }
620
+
621
+ const found = wires.route(search, i, options);
622
+
623
+ if (found !== null && found.cost < old_cost) {
624
+ held[i] = found;
625
+
626
+ moved++;
627
+ } else if (old !== null) {
628
+ // what it has is the best it can do here; remember that, so it is not asked again
629
+ // until something changes
630
+ old.cost = old_cost;
631
+ }
632
+
633
+ if (held[i] !== null) {
634
+ wires.occupy(i, held[i], 1);
635
+ }
636
+ }
637
+
638
+ return moved;
639
+ }
640
+
641
+ /**
642
+ * Say which wires are still sharing a line with somebody, and how many wires that is.
643
+ *
644
+ * A wire is asked to look again only when some part of its own route is contested — shared with
645
+ * a wire of another net; its own net is welcome. One that is clear of everybody has already found
646
+ * what it wanted, and re-deriving it costs a full search to arrive back where it started.
647
+ *
648
+ * @param {RouteGrid} grid
649
+ * @param {(WireRoute|null)[]} held
650
+ * @param {Int32Array} net of each wire
651
+ * @param {Uint8Array} contested written
652
+ * @returns {number} wires still sharing a line
653
+ */
654
+ function mark_contested(grid, held, net, contested) {
655
+ const nx = grid.nx;
656
+
657
+ let total = 0;
658
+
659
+ for (let i = 0; i < held.length; i++) {
660
+ const route = held[i];
661
+
662
+ if (route === null) {
663
+ // No route was found for it. Congestion is not what it is waiting for, but the grid it
664
+ // failed on is not the grid the next round presents, so it gets asked again rather than
665
+ // being written off on the strength of one attempt.
666
+ contested[i] = 1;
667
+
668
+ continue;
669
+ }
670
+
671
+ contested[i] = 0;
672
+
673
+ const path = route.path;
674
+
675
+ for (let k = 0; k + 1 < path.length; k++) {
676
+ const a = path[k];
677
+ const b = path[k + 1];
678
+
679
+ const shared = b === a + 1 ? grid.foreign_h(a, net[i]) > 0
680
+ : a === b + 1 ? grid.foreign_h(b, net[i]) > 0
681
+ : b === a + nx ? grid.foreign_v(a, net[i]) > 0
682
+ : grid.foreign_v(b, net[i]) > 0;
683
+
684
+ if (shared) {
685
+ contested[i] = 1;
686
+
687
+ total++;
688
+
689
+ break;
690
+ }
691
+ }
692
+ }
693
+
694
+ return total;
695
+ }
696
+
697
+ /**
698
+ * @param {RouteGrid} grid
699
+ * @param {number} rate
700
+ * @returns {void}
701
+ */
702
+ function remember(grid, rate) {
703
+ for (let i = 0; i < grid.vertex_count; i++) {
704
+ // a line is fought over when it holds wires beyond those of the net that holds it
705
+ const own_h = Math.max(grid.owned_h[i], 1);
706
+ const own_v = Math.max(grid.owned_v[i], 1);
707
+
708
+ if (grid.use_h[i] > own_h) {
709
+ grid.history_h[i] += rate * (grid.use_h[i] - own_h);
710
+ }
711
+
712
+ if (grid.use_v[i] > own_v) {
713
+ grid.history_v[i] += rate * (grid.use_v[i] - own_v);
714
+ }
715
+ }
716
+ }