@reicek/neataptic-ts 0.1.21 → 0.1.22

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 (223) hide show
  1. package/.github/agents/boundary-mapper.agent.md +29 -0
  2. package/.github/agents/docs-scout.agent.md +29 -0
  3. package/.github/agents/plan-scout.agent.md +29 -0
  4. package/.github/agents/solid-split.agent.md +138 -0
  5. package/.github/copilot-instructions.md +103 -0
  6. package/package.json +6 -3
  7. package/plans/ES2023 migration +13 -8
  8. package/plans/Evolution_Training_Interoperability_Contracts.md +1 -1
  9. package/plans/Interactive_Examples_and_Learning_Path.md +10 -2
  10. package/plans/Memory_Optimization.md +3 -3
  11. package/plans/README.md +63 -0
  12. package/plans/Roadmap.md +15 -3
  13. package/plans/asciiMaze_SOLID_split.done.md +130 -0
  14. package/plans/flappy_bird_SOLID_split.done.md +67 -0
  15. package/scripts/assets/theme.css +221 -34
  16. package/scripts/copy-examples.mjs +9 -5
  17. package/scripts/export-onnx.mjs +3 -3
  18. package/scripts/generate-bench-tables.mjs +10 -10
  19. package/scripts/generate-bench-tables.ts +10 -10
  20. package/scripts/generate-docs.ts +1415 -449
  21. package/scripts/render-docs-html.ts +15 -8
  22. package/src/README.md +101 -223
  23. package/src/architecture/README.md +57 -185
  24. package/src/architecture/layer/README.md +38 -38
  25. package/src/architecture/network/README.md +33 -31
  26. package/src/architecture/network/activate/README.md +77 -77
  27. package/src/architecture/network/connect/README.md +15 -13
  28. package/src/architecture/network/deterministic/README.md +7 -7
  29. package/src/architecture/network/evolve/README.md +44 -44
  30. package/src/architecture/network/gating/README.md +20 -20
  31. package/src/architecture/network/genetic/README.md +51 -51
  32. package/src/architecture/network/mutate/README.md +97 -97
  33. package/src/architecture/network/onnx/README.md +264 -264
  34. package/src/architecture/network/prune/README.md +39 -39
  35. package/src/architecture/network/remove/README.md +26 -26
  36. package/src/architecture/network/serialize/README.md +56 -56
  37. package/src/architecture/network/slab/README.md +61 -61
  38. package/src/architecture/network/standalone/README.md +24 -24
  39. package/src/architecture/network/stats/README.md +9 -9
  40. package/src/architecture/network/topology/README.md +46 -46
  41. package/src/architecture/network/training/README.md +21 -21
  42. package/src/methods/README.md +9 -87
  43. package/src/multithreading/README.md +8 -77
  44. package/src/multithreading/workers/README.md +2 -2
  45. package/src/multithreading/workers/browser/README.md +0 -6
  46. package/src/multithreading/workers/node/README.md +0 -3
  47. package/src/neat/README.md +562 -568
  48. package/src/utils/README.md +18 -18
  49. package/test/examples/asciiMaze/README.md +59 -59
  50. package/test/examples/asciiMaze/asciiMaze.e2e.test.ts +14 -9
  51. package/test/examples/asciiMaze/browser-entry/README.md +196 -0
  52. package/test/examples/asciiMaze/browser-entry/browser-entry.abort.services.ts +95 -0
  53. package/test/examples/asciiMaze/browser-entry/browser-entry.constants.ts +23 -0
  54. package/test/examples/asciiMaze/browser-entry/browser-entry.curriculum.services.ts +115 -0
  55. package/test/examples/asciiMaze/browser-entry/browser-entry.globals.services.ts +106 -0
  56. package/test/examples/asciiMaze/browser-entry/browser-entry.host.services.ts +157 -0
  57. package/test/examples/asciiMaze/browser-entry/browser-entry.services.ts +14 -0
  58. package/test/examples/asciiMaze/browser-entry/browser-entry.ts +129 -0
  59. package/test/examples/asciiMaze/browser-entry/browser-entry.types.ts +120 -0
  60. package/test/examples/asciiMaze/browser-entry/browser-entry.utils.ts +98 -0
  61. package/test/examples/asciiMaze/browser-entry.ts +10 -576
  62. package/test/examples/asciiMaze/dashboardManager/README.md +276 -0
  63. package/test/examples/asciiMaze/dashboardManager/archive/README.md +16 -0
  64. package/test/examples/asciiMaze/dashboardManager/archive/dashboardManager.archive.services.ts +267 -0
  65. package/test/examples/asciiMaze/dashboardManager/dashboardManager.constants.ts +35 -0
  66. package/test/examples/asciiMaze/dashboardManager/dashboardManager.services.ts +103 -0
  67. package/test/examples/asciiMaze/dashboardManager/dashboardManager.ts +181 -0
  68. package/test/examples/asciiMaze/dashboardManager/dashboardManager.types.ts +267 -0
  69. package/test/examples/asciiMaze/dashboardManager/dashboardManager.utils.ts +254 -0
  70. package/test/examples/asciiMaze/dashboardManager/live/README.md +14 -0
  71. package/test/examples/asciiMaze/dashboardManager/live/dashboardManager.live.services.ts +264 -0
  72. package/test/examples/asciiMaze/dashboardManager/telemetry/README.md +47 -0
  73. package/test/examples/asciiMaze/dashboardManager/telemetry/dashboardManager.telemetry.services.ts +513 -0
  74. package/test/examples/asciiMaze/dashboardManager.ts +13 -2335
  75. package/test/examples/asciiMaze/evolutionEngine/README.md +1058 -0
  76. package/test/examples/asciiMaze/evolutionEngine/curriculumPhase.ts +90 -0
  77. package/test/examples/asciiMaze/evolutionEngine/engineState.constants.ts +36 -0
  78. package/test/examples/asciiMaze/evolutionEngine/engineState.ts +58 -513
  79. package/test/examples/asciiMaze/evolutionEngine/engineState.types.ts +212 -0
  80. package/test/examples/asciiMaze/evolutionEngine/engineState.utils.ts +301 -0
  81. package/test/examples/asciiMaze/evolutionEngine/evolutionEngine.types.ts +445 -0
  82. package/test/examples/asciiMaze/evolutionEngine/evolutionLoop.ts +81 -50
  83. package/test/examples/asciiMaze/evolutionEngine/optionsAndSetup.ts +2 -4
  84. package/test/examples/asciiMaze/evolutionEngine/populationDynamics.ts +17 -33
  85. package/test/examples/asciiMaze/evolutionEngine/populationPruning.ts +1 -1
  86. package/test/examples/asciiMaze/evolutionEngine/rngAndTiming.ts +1 -2
  87. package/test/examples/asciiMaze/evolutionEngine/sampling.ts +1 -1
  88. package/test/examples/asciiMaze/evolutionEngine/scratchPools.ts +2 -5
  89. package/test/examples/asciiMaze/evolutionEngine/setupHelpers.ts +30 -37
  90. package/test/examples/asciiMaze/evolutionEngine/telemetryMetrics.ts +16 -58
  91. package/test/examples/asciiMaze/evolutionEngine/trainingWarmStart.ts +2 -2
  92. package/test/examples/asciiMaze/evolutionEngine.ts +55 -55
  93. package/test/examples/asciiMaze/fitness.ts +2 -2
  94. package/test/examples/asciiMaze/fitness.types.ts +65 -0
  95. package/test/examples/asciiMaze/interfaces.ts +64 -1352
  96. package/test/examples/asciiMaze/mazeMovement/README.md +356 -0
  97. package/test/examples/asciiMaze/mazeMovement/finalization/README.md +49 -0
  98. package/test/examples/asciiMaze/mazeMovement/finalization/mazeMovement.finalization.ts +138 -0
  99. package/test/examples/asciiMaze/mazeMovement/mazeMovement.constants.ts +101 -0
  100. package/test/examples/asciiMaze/mazeMovement/mazeMovement.services.ts +230 -0
  101. package/test/examples/asciiMaze/mazeMovement/mazeMovement.ts +299 -0
  102. package/test/examples/asciiMaze/mazeMovement/mazeMovement.types.ts +185 -0
  103. package/test/examples/asciiMaze/mazeMovement/mazeMovement.utils.ts +153 -0
  104. package/test/examples/asciiMaze/mazeMovement/policy/README.md +91 -0
  105. package/test/examples/asciiMaze/mazeMovement/policy/mazeMovement.policy.ts +467 -0
  106. package/test/examples/asciiMaze/mazeMovement/runtime/README.md +95 -0
  107. package/test/examples/asciiMaze/mazeMovement/runtime/mazeMovement.runtime.ts +354 -0
  108. package/test/examples/asciiMaze/mazeMovement/shaping/README.md +124 -0
  109. package/test/examples/asciiMaze/mazeMovement/shaping/mazeMovement.shaping.ts +459 -0
  110. package/test/examples/asciiMaze/mazeMovement.ts +12 -2978
  111. package/test/examples/flappy_bird/Trace-20260309T191949.json +24124 -0
  112. package/test/examples/flappy_bird/browser-entry/README.md +1129 -0
  113. package/test/examples/flappy_bird/browser-entry/browser-entry.host.utils.ts +4 -324
  114. package/test/examples/flappy_bird/browser-entry/browser-entry.network-view.utils.ts +6 -399
  115. package/test/examples/flappy_bird/browser-entry/browser-entry.playback.utils.ts +1 -717
  116. package/test/examples/flappy_bird/browser-entry/browser-entry.spawn.utils.ts +11 -31
  117. package/test/examples/flappy_bird/browser-entry/browser-entry.visualization.utils.ts +15 -893
  118. package/test/examples/flappy_bird/browser-entry/host/README.md +307 -0
  119. package/test/examples/flappy_bird/browser-entry/host/host.resize.service.ts +1 -295
  120. package/test/examples/flappy_bird/browser-entry/host/host.ts +562 -6
  121. package/test/examples/flappy_bird/browser-entry/host/resize/README.md +274 -0
  122. package/test/examples/flappy_bird/browser-entry/host/resize/host.resize.service.constants.ts +31 -0
  123. package/test/examples/flappy_bird/browser-entry/host/resize/host.resize.service.services.ts +360 -0
  124. package/test/examples/flappy_bird/browser-entry/host/resize/host.resize.service.ts +117 -0
  125. package/test/examples/flappy_bird/browser-entry/host/resize/host.resize.service.types.ts +63 -0
  126. package/test/examples/flappy_bird/browser-entry/host/resize/host.resize.service.utils.ts +250 -0
  127. package/test/examples/flappy_bird/browser-entry/network-view/README.md +399 -0
  128. package/test/examples/flappy_bird/browser-entry/network-view/network-view.topology.utils.ts +255 -0
  129. package/test/examples/flappy_bird/browser-entry/network-view/network-view.ts +802 -7
  130. package/test/examples/flappy_bird/browser-entry/playback/README.md +684 -0
  131. package/test/examples/flappy_bird/browser-entry/playback/background/README.md +277 -0
  132. package/test/examples/flappy_bird/browser-entry/playback/background/ground-grid/README.md +770 -0
  133. package/test/examples/flappy_bird/browser-entry/playback/background/ground-grid/playback.background.ground-grid.cache.services.ts +178 -0
  134. package/test/examples/flappy_bird/browser-entry/playback/background/ground-grid/playback.background.ground-grid.constants.ts +107 -0
  135. package/test/examples/flappy_bird/browser-entry/playback/background/ground-grid/playback.background.ground-grid.geometry.utils.ts +518 -0
  136. package/test/examples/flappy_bird/browser-entry/playback/background/ground-grid/playback.background.ground-grid.math.utils.ts +117 -0
  137. package/test/examples/flappy_bird/browser-entry/playback/background/ground-grid/playback.background.ground-grid.pulse.utils.ts +233 -0
  138. package/test/examples/flappy_bird/browser-entry/playback/background/ground-grid/playback.background.ground-grid.services.ts +211 -0
  139. package/test/examples/flappy_bird/browser-entry/playback/background/ground-grid/playback.background.ground-grid.ts +48 -0
  140. package/test/examples/flappy_bird/browser-entry/playback/background/ground-grid/playback.background.ground-grid.types.ts +212 -0
  141. package/test/examples/flappy_bird/browser-entry/playback/background/ground-grid/playback.background.ground-grid.utils.ts +81 -0
  142. package/test/examples/flappy_bird/browser-entry/playback/background/playback.background.cache.services.ts +96 -0
  143. package/test/examples/flappy_bird/browser-entry/playback/background/playback.background.constants.ts +62 -0
  144. package/test/examples/flappy_bird/browser-entry/playback/background/playback.background.services.ts +244 -0
  145. package/test/examples/flappy_bird/browser-entry/playback/background/playback.background.ts +53 -0
  146. package/test/examples/flappy_bird/browser-entry/playback/background/playback.background.types.ts +68 -0
  147. package/test/examples/flappy_bird/browser-entry/playback/background/playback.background.utils.ts +100 -0
  148. package/test/examples/flappy_bird/browser-entry/playback/frame-render/README.md +310 -0
  149. package/test/examples/flappy_bird/browser-entry/playback/frame-render/playback.frame-render.service.ts +92 -0
  150. package/test/examples/flappy_bird/browser-entry/playback/frame-render/playback.frame-render.services.ts +272 -0
  151. package/test/examples/flappy_bird/browser-entry/playback/frame-render/playback.frame-render.types.ts +39 -0
  152. package/test/examples/flappy_bird/browser-entry/playback/frame-render/playback.frame-render.utils.ts +493 -0
  153. package/test/examples/flappy_bird/browser-entry/playback/playback.constants.ts +1 -1
  154. package/test/examples/flappy_bird/browser-entry/playback/playback.frame-render.service.ts +4 -0
  155. package/test/examples/flappy_bird/browser-entry/playback/playback.snapshot.utils.ts +44 -0
  156. package/test/examples/flappy_bird/browser-entry/playback/playback.starfield.service.ts +39 -122
  157. package/test/examples/flappy_bird/browser-entry/playback/playback.starfield.services.ts +272 -0
  158. package/test/examples/flappy_bird/browser-entry/playback/playback.starfield.types.ts +62 -0
  159. package/test/examples/flappy_bird/browser-entry/playback/playback.starfield.utils.ts +11 -4
  160. package/test/examples/flappy_bird/browser-entry/playback/playback.ts +409 -8
  161. package/test/examples/flappy_bird/browser-entry/playback/playback.types.ts +4 -12
  162. package/test/examples/flappy_bird/browser-entry/runtime/README.md +235 -0
  163. package/test/examples/flappy_bird/browser-entry/runtime/runtime.evolution-launch.service.ts +45 -0
  164. package/test/examples/flappy_bird/browser-entry/runtime/runtime.lifecycle.service.ts +81 -0
  165. package/test/examples/flappy_bird/browser-entry/runtime/runtime.startup.service.ts +74 -0
  166. package/test/examples/flappy_bird/browser-entry/runtime/runtime.ts +31 -121
  167. package/test/examples/flappy_bird/browser-entry/runtime/runtime.types.ts +36 -0
  168. package/test/examples/flappy_bird/browser-entry/visualization/README.md +557 -0
  169. package/test/examples/flappy_bird/browser-entry/visualization/visualization.constants.ts +110 -0
  170. package/test/examples/flappy_bird/browser-entry/visualization/visualization.draw.service.ts +957 -19
  171. package/test/examples/flappy_bird/browser-entry/visualization/visualization.legend.utils.ts +138 -3
  172. package/test/examples/flappy_bird/browser-entry/visualization/visualization.topology.utils.ts +3 -27
  173. package/test/examples/flappy_bird/browser-entry/visualization/visualization.ts +1 -23
  174. package/test/examples/flappy_bird/browser-entry/worker-channel/README.md +156 -0
  175. package/test/examples/flappy_bird/constants/README.md +1179 -0
  176. package/test/examples/flappy_bird/constants/constants.network-view.ts +24 -0
  177. package/test/examples/flappy_bird/constants/constants.palette.ts +7 -0
  178. package/test/examples/flappy_bird/constants/constants.starfield.ts +78 -3
  179. package/test/examples/flappy_bird/environment/README.md +143 -0
  180. package/test/examples/flappy_bird/environment/environment.observation.utils.ts +1 -19
  181. package/test/examples/flappy_bird/environment/environment.step.service.ts +3 -66
  182. package/test/examples/flappy_bird/evaluation/README.md +130 -0
  183. package/test/examples/flappy_bird/evaluation/evaluation.fitness.utils.ts +1 -1
  184. package/test/examples/flappy_bird/evaluation/evaluation.rollout.service.ts +5 -375
  185. package/test/examples/flappy_bird/evaluation/rollout/README.md +291 -0
  186. package/test/examples/flappy_bird/evaluation/rollout/evaluation.rollout.constants.ts +30 -0
  187. package/test/examples/flappy_bird/evaluation/rollout/evaluation.rollout.service.ts +58 -0
  188. package/test/examples/flappy_bird/evaluation/rollout/evaluation.rollout.services.ts +310 -0
  189. package/test/examples/flappy_bird/evaluation/rollout/evaluation.rollout.types.ts +56 -0
  190. package/test/examples/flappy_bird/evaluation/rollout/evaluation.rollout.utils.ts +368 -0
  191. package/test/examples/flappy_bird/flappy-evolution-worker/README.md +618 -0
  192. package/test/examples/flappy_bird/flappy-evolution-worker/flappy-evolution-worker.playback.service.ts +7 -7
  193. package/test/examples/flappy_bird/flappy-evolution-worker/flappy-evolution-worker.simulation.frame.service.ts +364 -0
  194. package/test/examples/flappy_bird/flappy-evolution-worker/flappy-evolution-worker.simulation.types.ts +14 -0
  195. package/test/examples/flappy_bird/flappy-evolution-worker/flappy-evolution-worker.simulation.utils.ts +4 -201
  196. package/test/examples/flappy_bird/flappy-evolution-worker/flappy-evolution-worker.ts +184 -345
  197. package/test/examples/flappy_bird/flappy-evolution-worker/flappy-evolution-worker.warm-start.service.ts +291 -0
  198. package/test/examples/flappy_bird/flappy.simulation.shared.utils.ts +5 -0
  199. package/test/examples/flappy_bird/simulation-shared/README.md +417 -0
  200. package/test/examples/flappy_bird/simulation-shared/observation/README.md +183 -0
  201. package/test/examples/flappy_bird/simulation-shared/observation/observation.features.utils.ts +301 -0
  202. package/test/examples/flappy_bird/simulation-shared/observation/observation.ts +9 -0
  203. package/test/examples/flappy_bird/simulation-shared/observation/observation.vector.utils.ts +59 -0
  204. package/test/examples/flappy_bird/simulation-shared/simulation-shared.observation.utils.ts +5 -403
  205. package/test/examples/flappy_bird/simulation-shared/simulation-shared.spawn.utils.ts +20 -6
  206. package/test/examples/flappy_bird/{evaluation/evaluation.statistics.utils.ts → simulation-shared/simulation-shared.statistics.utils.ts} +23 -8
  207. package/test/examples/flappy_bird/trainer/README.md +563 -0
  208. package/test/examples/flappy_bird/trainer/evaluation/README.md +199 -0
  209. package/test/examples/flappy_bird/trainer/evaluation/trainer.evaluation.service.constants.ts +9 -0
  210. package/test/examples/flappy_bird/trainer/evaluation/trainer.evaluation.service.services.ts +73 -0
  211. package/test/examples/flappy_bird/trainer/evaluation/trainer.evaluation.service.ts +165 -0
  212. package/test/examples/flappy_bird/trainer/evaluation/trainer.evaluation.service.types.ts +25 -0
  213. package/test/examples/flappy_bird/trainer/evaluation/trainer.evaluation.service.utils.ts +161 -0
  214. package/test/examples/flappy_bird/trainer/trainer.evaluation.service.ts +13 -0
  215. package/test/examples/flappy_bird/trainer/trainer.report.service.services.ts +181 -0
  216. package/test/examples/flappy_bird/trainer/trainer.report.service.ts +126 -0
  217. package/test/examples/flappy_bird/trainer/trainer.selection.utils.ts +89 -0
  218. package/test/examples/flappy_bird/trainer/trainer.ts +11 -553
  219. package/test/examples/flappy_bird/browser-entry/browser-entry.utils.ts +0 -12
  220. package/test/examples/flappy_bird/environment/environment.ts +0 -7
  221. package/test/examples/flappy_bird/evaluation/evaluation.ts +0 -7
  222. package/test/examples/flappy_bird/simulation-shared/simulation-shared.ts +0 -15
  223. package/test/examples/flappy_bird/trainer/trainer.statistics.utils.ts +0 -78
@@ -1,717 +1 @@
1
- import {
2
- resolveAliveBirdCount,
3
- resolveLeaderPipesPassed,
4
- } from './browser-entry.observation.utils';
5
- import {
6
- resolveVisibleWorldHeightPx,
7
- resolveVisibleWorldWidthPx,
8
- resolveWorldViewport,
9
- } from './browser-entry.viewport.utils';
10
- import { requestWorkerPlaybackStep } from './worker-channel/worker-channel';
11
- import {
12
- FLAPPY_EMULATION_SPEED_MULTIPLIER,
13
- FLAPPY_BIRD_VIEWPORT_X_RATIO,
14
- FLAPPY_BIRD_CHAMPION_SHINE_FILL_STYLE,
15
- FLAPPY_BIRD_CHAMPION_SHINE_GLOW_COLOR,
16
- FLAPPY_BIRD_SHINE_FILL_STYLE,
17
- FLAPPY_BIRD_SHINE_INSET_RATIO,
18
- FLAPPY_BIRD_SHINE_SIZE_RATIO,
19
- FLAPPY_BIRD_WHITE_SHINE_GLOW_COLOR,
20
- FLAPPY_BIRD_WHITE_SHINE_GLOW_BLUR_PX,
21
- FLAPPY_BIRD_AURA_ALPHA,
22
- FLAPPY_BIRD_AURA_BLUR_MULTIPLIER,
23
- FLAPPY_BIRD_AURA_EXPAND_PX,
24
- FLAPPY_BIRD_BODY_GLOW_BLUR_PX,
25
- FLAPPY_BIRD_CHAMPION_EXTRA_GLOW_BLUR_PX,
26
- FLAPPY_BIRD_CHAMPION_RED_GLOW_ALPHA,
27
- FLAPPY_BIRD_CHAMPION_RED_GLOW_EXPAND_PX,
28
- FLAPPY_LEADER_RING_LINE_WIDTH_PX,
29
- FLAPPY_LEADER_RING_GLOW_BLUR_PX,
30
- FLAPPY_LEADER_RING_RADIUS_OFFSET_PX,
31
- FLAPPY_NON_CHAMPION_OPACITY,
32
- FLAPPY_TRAIL_LINE_WIDTH_PX,
33
- FLAPPY_TRAIL_MIN_HORIZONTAL_SEGMENT_PX,
34
- FLAPPY_TRAIL_MIN_VERTICAL_SEGMENT_PX,
35
- FLAPPY_NEON_PALETTE,
36
- } from '../constants/constants';
37
- import type {
38
- EvolutionPlaybackStepSnapshot,
39
- PlaybackFrameStats,
40
- PopulationRenderState,
41
- TrailPoint,
42
- TrailState,
43
- } from './browser-entry.types';
44
- import {
45
- FLAPPY_PIPE_SPEED_PX_PER_FRAME,
46
- FLAPPY_PIPE_WIDTH_PX,
47
- FLAPPY_BIRD_X_PX,
48
- FLAPPY_BIRD_RADIUS_PX,
49
- FLAPPY_TRAIL_OPACITY_FACTOR,
50
- } from '../constants/constants';
51
- import { nextAnimationFrame } from './playback/playback.loop.service';
52
- import { resolveStarfieldTiles } from './playback/playback.starfield.service';
53
- import { positiveModulo } from './playback/playback.starfield.utils';
54
- import {
55
- pushTrailPoint,
56
- resolveEdgeOpacityFactor,
57
- resolveTrailLifetimeOpacityFactor,
58
- } from './playback/playback.trail.utils';
59
- import type { PlaybackEdgeBounds } from './playback/playback.types';
60
- import {
61
- resolvePlaybackCompletionSummary,
62
- resolvePlaybackFrameStats,
63
- resolvePlaybackStepRequest,
64
- } from './playback/playback.worker-channel.utils';
65
- import {
66
- resolveBirdRenderStyle,
67
- resolveChampionBirdIndex,
68
- } from './playback/playback.render.utils';
69
- import { drawPipeNeonOutline } from './playback/playback.render.service';
70
-
71
- /**
72
- * Renders all birds from the current generation in one shared world.
73
- *
74
- * @param canvas - Target playback canvas.
75
- * @param context - Canvas 2D context.
76
- * @param evolutionWorker - Worker owning playback state.
77
- * @param onFrameStats - Frame telemetry callback.
78
- * @returns Aggregate playback summary.
79
- */
80
- export async function animatePopulationEpisodeInternal(
81
- canvas: HTMLCanvasElement,
82
- context: CanvasRenderingContext2D,
83
- evolutionWorker: Worker,
84
- onFrameStats: (stats: PlaybackFrameStats) => void,
85
- ): Promise<{
86
- averagePipesPassed: number;
87
- p90FramesSurvived: number;
88
- winnerPipesPassed: number;
89
- winnerFramesSurvived: number;
90
- }> {
91
- // Step 1: Initialize worker playback state with current viewport width.
92
- evolutionWorker.postMessage({
93
- type: 'start-playback',
94
- payload: {
95
- visibleWorldWidthPx: resolveVisibleWorldWidthPx(canvas),
96
- visibleWorldHeightPx: resolveVisibleWorldHeightPx(canvas),
97
- },
98
- });
99
-
100
- // Step 2: Initialize local render/trail state mirrors.
101
- const renderState: PopulationRenderState = {
102
- frameIndex: 0,
103
- visibleWorldWidthPx: resolveVisibleWorldWidthPx(canvas),
104
- visibleWorldHeightPx: resolveVisibleWorldHeightPx(canvas),
105
- nextPipeId: 0,
106
- lastSpawnedPipeGapPx: 0,
107
- lastSpawnedPipeGapCenterYPx: 0,
108
- lastSpawnedPipeSpawnIntervalFrames: 0,
109
- framesUntilNextPipeSpawn: 0,
110
- pipes: [],
111
- birds: [],
112
- };
113
- const trailState: TrailState = {
114
- birdTrailsY: [],
115
- };
116
- let simulationFrameBudget = 0;
117
- let finished = false;
118
- let averagePipesPassed = 0;
119
- let p90FramesSurvived = 0;
120
- let winnerPipesPassed = 0;
121
- let winnerFramesSurvived = 0;
122
- let latestLeaderPipesPassed = 0;
123
- let latestLeaderFramesSurvived = 0;
124
-
125
- // Step 3: Run playback batches until worker reports completion.
126
- while (!finished) {
127
- // Step 3.1: Resolve frame budget and request one playback step batch.
128
- renderState.visibleWorldWidthPx = resolveVisibleWorldWidthPx(canvas);
129
- renderState.visibleWorldHeightPx = resolveVisibleWorldHeightPx(canvas);
130
- const { simulationFrameBudgetRemainder, playbackStepRequest } =
131
- resolvePlaybackStepRequest({
132
- simulationFrameBudget,
133
- visibleWorldWidthPx: renderState.visibleWorldWidthPx,
134
- visibleWorldHeightPx: renderState.visibleWorldHeightPx,
135
- emulationSpeedMultiplier: FLAPPY_EMULATION_SPEED_MULTIPLIER,
136
- });
137
- simulationFrameBudget = simulationFrameBudgetRemainder;
138
-
139
- const playbackStepPayload = await requestWorkerPlaybackStep(
140
- evolutionWorker,
141
- playbackStepRequest,
142
- );
143
-
144
- // Step 3.2: Apply state snapshot and update visual trail cache.
145
- applyPlaybackSnapshot(renderState, playbackStepPayload.snapshot);
146
- updateTrailState(trailState, renderState);
147
-
148
- // Step 3.3: Resolve leader metrics and emit frame telemetry.
149
- const leaderPipesPassed = resolveLeaderPipesPassed(renderState.birds);
150
- const leaderFramesSurvived = resolveLeaderFramesSurvived(renderState);
151
- latestLeaderPipesPassed = leaderPipesPassed;
152
- latestLeaderFramesSurvived = leaderFramesSurvived;
153
- onFrameStats(
154
- resolvePlaybackFrameStats(
155
- playbackStepPayload,
156
- renderState.frameIndex,
157
- resolveAliveBirdCount(renderState.birds),
158
- leaderPipesPassed,
159
- leaderFramesSurvived,
160
- ),
161
- );
162
-
163
- // Step 3.4: Fold end-of-playback aggregates when worker signals done.
164
- if (playbackStepPayload.done) {
165
- const playbackCompletionSummary = resolvePlaybackCompletionSummary(
166
- playbackStepPayload,
167
- latestLeaderPipesPassed,
168
- latestLeaderFramesSurvived,
169
- );
170
- finished = true;
171
- averagePipesPassed = playbackCompletionSummary.averagePipesPassed;
172
- p90FramesSurvived = playbackCompletionSummary.p90FramesSurvived;
173
- winnerPipesPassed = playbackCompletionSummary.winnerPipesPassed;
174
- winnerFramesSurvived = playbackCompletionSummary.winnerFramesSurvived;
175
- }
176
-
177
- // Step 3.5: Render frame and yield to browser RAF when still active.
178
- renderPopulationFrame(context, renderState, trailState);
179
- if (!finished) {
180
- await nextAnimationFrame();
181
- }
182
- }
183
-
184
- // Step 4: Render final settled frame and return aggregate summary.
185
- renderPopulationFrame(context, renderState, trailState);
186
-
187
- return {
188
- averagePipesPassed,
189
- p90FramesSurvived,
190
- winnerPipesPassed,
191
- winnerFramesSurvived,
192
- };
193
- }
194
-
195
- /**
196
- * Applies worker snapshot data to the local render state.
197
- *
198
- * @param renderState - Mutable render state.
199
- * @param snapshot - Worker playback snapshot.
200
- * @returns Nothing.
201
- */
202
- function applyPlaybackSnapshot(
203
- renderState: PopulationRenderState,
204
- snapshot: EvolutionPlaybackStepSnapshot,
205
- ): void {
206
- renderState.frameIndex = snapshot.frameIndex;
207
- renderState.visibleWorldWidthPx = snapshot.visibleWorldWidthPx;
208
- renderState.visibleWorldHeightPx = snapshot.visibleWorldHeightPx;
209
- renderState.pipes = snapshot.pipes;
210
- renderState.birds = snapshot.birds.map((birdSnapshot) => ({
211
- color: birdSnapshot.color,
212
- yPx: birdSnapshot.yPx,
213
- pipesPassed: birdSnapshot.pipesPassed,
214
- framesSurvived: birdSnapshot.framesSurvived,
215
- done: birdSnapshot.done,
216
- }));
217
- }
218
-
219
- /**
220
- * Draws one simulation frame for the current population state.
221
- *
222
- * @param context - Canvas 2D drawing context.
223
- * @param renderState - Mutable simulation state snapshot.
224
- * @param trailState - Leader trail render cache.
225
- * @returns Nothing.
226
- */
227
- function renderPopulationFrame(
228
- context: CanvasRenderingContext2D,
229
- renderState: PopulationRenderState,
230
- trailState: TrailState,
231
- ): void {
232
- // Step 1: Resolve viewport transform and clear target canvas.
233
- const viewport = resolveWorldViewport(context.canvas);
234
- const visibleWorldWidthPx = Math.max(1, renderState.visibleWorldWidthPx);
235
- const visibleWorldHeightPx = Math.max(1, renderState.visibleWorldHeightPx);
236
- const desiredBirdScreenXPx =
237
- visibleWorldWidthPx * FLAPPY_BIRD_VIEWPORT_X_RATIO;
238
- const cameraLeftPx = FLAPPY_BIRD_X_PX - desiredBirdScreenXPx;
239
-
240
- // Reset state that can suppress shadows between frames.
241
- context.globalAlpha = 1;
242
- context.globalCompositeOperation = 'source-over';
243
- context.shadowBlur = 0;
244
- context.shadowColor = 'transparent';
245
- context.shadowOffsetX = 0;
246
- context.shadowOffsetY = 0;
247
-
248
- context.clearRect(0, 0, context.canvas.width, context.canvas.height);
249
-
250
- context.save();
251
- context.translate(viewport.offsetXPx, viewport.offsetYPx);
252
- context.scale(viewport.scale, viewport.scale);
253
- context.translate(-cameraLeftPx, 0);
254
-
255
- // Step 2: Draw Radiant-style parallax background.
256
- drawParallaxBackground(context, renderState);
257
-
258
- // Step 3: Draw pipes with neon outlines.
259
- for (const pipe of renderState.pipes) {
260
- const gapHalf = pipe.gapSizePx * 0.5;
261
- const pipeLeft = pipe.xPx;
262
- const gapTop = pipe.gapCenterYPx - gapHalf;
263
- const gapBottom = pipe.gapCenterYPx + gapHalf;
264
-
265
- context.fillStyle = FLAPPY_NEON_PALETTE.pipeFill;
266
- context.fillRect(pipeLeft, 0, FLAPPY_PIPE_WIDTH_PX, gapTop);
267
- drawPipeNeonOutline(context, pipeLeft, 0, FLAPPY_PIPE_WIDTH_PX, gapTop);
268
- context.fillRect(
269
- pipeLeft,
270
- gapBottom,
271
- FLAPPY_PIPE_WIDTH_PX,
272
- visibleWorldHeightPx - gapBottom,
273
- );
274
- drawPipeNeonOutline(
275
- context,
276
- pipeLeft,
277
- gapBottom,
278
- FLAPPY_PIPE_WIDTH_PX,
279
- visibleWorldHeightPx - gapBottom,
280
- );
281
- }
282
-
283
- // Step 4: Resolve champion bird and render bird bodies/shine/rings.
284
- const championBirdIndex = resolveChampionBirdIndex(renderState);
285
-
286
- renderState.birds.forEach((bird, birdIndex) => {
287
- if (bird.done) {
288
- return;
289
- }
290
-
291
- const birdSideLengthPx = Math.max(1, Math.round(FLAPPY_BIRD_RADIUS_PX * 2));
292
- const birdLeftPx = Math.round(FLAPPY_BIRD_X_PX - FLAPPY_BIRD_RADIUS_PX);
293
- const birdTopPx = Math.round(bird.yPx - FLAPPY_BIRD_RADIUS_PX);
294
- const { birdOpacity, birdRenderColor, isChampionBird } =
295
- resolveBirdRenderStyle(birdIndex, championBirdIndex);
296
-
297
- // Step 4.1: Add a soft Radiant-style aura plate behind the champion bird.
298
- if (isChampionBird) {
299
- const auraExpandPx = Math.round(FLAPPY_BIRD_AURA_EXPAND_PX);
300
- const previousCompositeOperation = context.globalCompositeOperation;
301
- context.globalCompositeOperation = 'lighter';
302
- context.globalAlpha = birdOpacity * FLAPPY_BIRD_AURA_ALPHA;
303
- context.fillStyle = birdRenderColor;
304
- context.shadowColor = birdRenderColor;
305
- context.shadowBlur = Math.round(
306
- FLAPPY_BIRD_BODY_GLOW_BLUR_PX * FLAPPY_BIRD_AURA_BLUR_MULTIPLIER,
307
- );
308
- context.fillRect(
309
- birdLeftPx - auraExpandPx,
310
- birdTopPx - auraExpandPx,
311
- birdSideLengthPx + auraExpandPx * 2,
312
- birdSideLengthPx + auraExpandPx * 2,
313
- );
314
- context.shadowBlur = 0;
315
- context.shadowColor = 'transparent';
316
- context.globalCompositeOperation = previousCompositeOperation;
317
- }
318
-
319
- if (isChampionBird) {
320
- const expandedGlowInsetPx = Math.round(
321
- FLAPPY_BIRD_CHAMPION_RED_GLOW_EXPAND_PX,
322
- );
323
- context.globalAlpha = birdOpacity * FLAPPY_BIRD_CHAMPION_RED_GLOW_ALPHA;
324
- context.fillStyle = FLAPPY_NEON_PALETTE.championBird;
325
- context.shadowColor = FLAPPY_NEON_PALETTE.championBird;
326
- context.shadowBlur =
327
- FLAPPY_BIRD_BODY_GLOW_BLUR_PX + FLAPPY_BIRD_CHAMPION_EXTRA_GLOW_BLUR_PX;
328
- context.fillRect(
329
- birdLeftPx - expandedGlowInsetPx,
330
- birdTopPx - expandedGlowInsetPx,
331
- birdSideLengthPx + expandedGlowInsetPx * 2,
332
- birdSideLengthPx + expandedGlowInsetPx * 2,
333
- );
334
- }
335
-
336
- context.globalAlpha = birdOpacity;
337
- context.fillStyle = birdRenderColor;
338
- context.shadowColor = birdRenderColor;
339
- context.shadowBlur =
340
- FLAPPY_BIRD_BODY_GLOW_BLUR_PX +
341
- (isChampionBird ? FLAPPY_BIRD_CHAMPION_EXTRA_GLOW_BLUR_PX : 0);
342
- context.fillRect(birdLeftPx, birdTopPx, birdSideLengthPx, birdSideLengthPx);
343
-
344
- const shineInsetPx = birdSideLengthPx * FLAPPY_BIRD_SHINE_INSET_RATIO;
345
- const shineSideLengthPx = Math.max(
346
- 1,
347
- birdSideLengthPx * FLAPPY_BIRD_SHINE_SIZE_RATIO,
348
- );
349
- context.fillStyle = isChampionBird
350
- ? FLAPPY_BIRD_CHAMPION_SHINE_FILL_STYLE
351
- : FLAPPY_BIRD_SHINE_FILL_STYLE;
352
- context.shadowColor = isChampionBird
353
- ? FLAPPY_BIRD_CHAMPION_SHINE_GLOW_COLOR
354
- : FLAPPY_BIRD_WHITE_SHINE_GLOW_COLOR;
355
- context.shadowBlur = FLAPPY_BIRD_WHITE_SHINE_GLOW_BLUR_PX;
356
- context.fillRect(
357
- Math.round(birdLeftPx + shineInsetPx),
358
- Math.round(birdTopPx + shineInsetPx),
359
- Math.round(shineSideLengthPx),
360
- Math.round(shineSideLengthPx),
361
- );
362
- context.shadowBlur = 0;
363
- context.shadowColor = 'transparent';
364
-
365
- if (isChampionBird) {
366
- context.strokeStyle = FLAPPY_NEON_PALETTE.leaderRing;
367
- context.lineWidth = FLAPPY_LEADER_RING_LINE_WIDTH_PX;
368
- context.shadowColor = FLAPPY_NEON_PALETTE.leaderRing;
369
- context.shadowBlur = FLAPPY_LEADER_RING_GLOW_BLUR_PX;
370
- const leaderRingInsetPx = Math.round(FLAPPY_LEADER_RING_RADIUS_OFFSET_PX);
371
- context.strokeRect(
372
- birdLeftPx - leaderRingInsetPx,
373
- birdTopPx - leaderRingInsetPx,
374
- birdSideLengthPx + leaderRingInsetPx * 2,
375
- birdSideLengthPx + leaderRingInsetPx * 2,
376
- );
377
- context.shadowBlur = 0;
378
- context.shadowColor = 'transparent';
379
- }
380
- });
381
-
382
- // Step 5: Draw stepped trails for active birds.
383
- renderState.birds.forEach((bird, birdIndex) => {
384
- if (bird.done) {
385
- return;
386
- }
387
-
388
- const birdTrailPoints = trailState.birdTrailsY[birdIndex];
389
- if (!birdTrailPoints || birdTrailPoints.length === 0) {
390
- return;
391
- }
392
-
393
- const parentBirdOpacity =
394
- birdIndex === championBirdIndex ? 1 : FLAPPY_NON_CHAMPION_OPACITY;
395
- const birdTrailColor =
396
- birdIndex === championBirdIndex
397
- ? FLAPPY_NEON_PALETTE.trail
398
- : FLAPPY_NEON_PALETTE.nonChampionBird;
399
-
400
- drawTrail(
401
- context,
402
- birdTrailPoints,
403
- birdTrailColor,
404
- FLAPPY_BIRD_X_PX - FLAPPY_BIRD_RADIUS_PX,
405
- parentBirdOpacity * FLAPPY_TRAIL_OPACITY_FACTOR,
406
- {
407
- leftXPx: cameraLeftPx,
408
- rightXPx: cameraLeftPx + visibleWorldWidthPx,
409
- topYPx: 0,
410
- bottomYPx: visibleWorldHeightPx,
411
- },
412
- );
413
- });
414
-
415
- context.globalAlpha = 1;
416
- // Step 6: Restore context to pre-viewport transform state.
417
- context.restore();
418
- }
419
-
420
- function drawParallaxBackground(
421
- context: CanvasRenderingContext2D,
422
- renderState: PopulationRenderState,
423
- ): void {
424
- const visibleWorldWidthPx = Math.max(
425
- 1,
426
- Math.round(renderState.visibleWorldWidthPx),
427
- );
428
- const scrollBasePx = renderState.frameIndex * FLAPPY_PIPE_SPEED_PX_PER_FRAME;
429
-
430
- // Step 1: Paint the background fill.
431
- context.globalAlpha = 1;
432
- context.globalCompositeOperation = 'source-over';
433
- context.shadowBlur = 0;
434
- context.shadowColor = 'transparent';
435
- context.fillStyle = FLAPPY_NEON_PALETTE.background;
436
- const visibleWorldHeightPx = Math.max(
437
- 1,
438
- Math.round(renderState.visibleWorldHeightPx),
439
- );
440
- context.fillRect(0, 0, visibleWorldWidthPx, visibleWorldHeightPx);
441
-
442
- // Step 2: Draw cached square-particle starfield layers with subtle parallax.
443
- // Each layer is pre-rendered into a tile and repeated via drawImage, which is
444
- // much faster than drawing dozens of blurred particles every frame.
445
- context.globalCompositeOperation = 'lighter';
446
- const starfieldTiles = resolveStarfieldTiles(visibleWorldHeightPx);
447
- for (const tile of starfieldTiles) {
448
- const scrollOffsetPx = scrollBasePx * tile.scrollRatio;
449
- drawTiledImageRow(context, {
450
- tile: tile.image,
451
- tileWidthPx: tile.tileWidthPx,
452
- visibleWidthPx: visibleWorldWidthPx,
453
- offsetPx: scrollOffsetPx,
454
- });
455
- }
456
- context.globalCompositeOperation = 'source-over';
457
- }
458
-
459
- function drawTiledImageRow(
460
- context: CanvasRenderingContext2D,
461
- layer: {
462
- tile: CanvasImageSource;
463
- tileWidthPx: number;
464
- visibleWidthPx: number;
465
- offsetPx: number;
466
- },
467
- ): void {
468
- const normalizedOffsetPx = positiveModulo(layer.offsetPx, layer.tileWidthPx);
469
- const maxTileIndex = Math.ceil(layer.visibleWidthPx / layer.tileWidthPx) + 1;
470
-
471
- for (let tileIndex = -1; tileIndex <= maxTileIndex; tileIndex += 1) {
472
- const tileLeftPx = tileIndex * layer.tileWidthPx - normalizedOffsetPx;
473
- context.drawImage(layer.tile, tileLeftPx, 0);
474
- }
475
- }
476
-
477
- /**
478
- * Updates the trail cache from the latest frame snapshot.
479
- *
480
- * @param trailState - Mutable trail state.
481
- * @param renderState - Current render state.
482
- * @returns Nothing.
483
- */
484
- function updateTrailState(
485
- trailState: TrailState,
486
- renderState: PopulationRenderState,
487
- ): void {
488
- renderState.birds.forEach((bird, birdIndex) => {
489
- if (!trailState.birdTrailsY[birdIndex]) {
490
- trailState.birdTrailsY[birdIndex] = [];
491
- }
492
- const birdTrail = trailState.birdTrailsY[birdIndex];
493
-
494
- if (bird.done) {
495
- birdTrail.length = 0;
496
- return;
497
- }
498
-
499
- pushTrailPoint(birdTrail, renderState.frameIndex, bird.yPx);
500
- });
501
- }
502
-
503
- /**
504
- * Renders a stepped trail polyline.
505
- *
506
- * @param context - Canvas 2D context.
507
- * @param trailPoints - Ordered trail points.
508
- * @param color - Trail color.
509
- * @param anchorX - Latest point x-anchor.
510
- * @returns Nothing.
511
- */
512
- function drawTrail(
513
- context: CanvasRenderingContext2D,
514
- trailPoints: TrailPoint[],
515
- color: string,
516
- anchorX: number,
517
- baseOpacity: number,
518
- edgeBounds: PlaybackEdgeBounds,
519
- ): void {
520
- // Step 1: Guard empty trails.
521
- if (trailPoints.length === 0) {
522
- return;
523
- }
524
-
525
- const latestTrailFrameIndex = trailPoints.at(-1)?.frameIndex ?? 0;
526
-
527
- const firstTrailPoint = trailPoints[0];
528
- const firstTrailFrameOffset = Math.max(
529
- 0,
530
- latestTrailFrameIndex - firstTrailPoint.frameIndex,
531
- );
532
- let previousXPosition =
533
- anchorX - firstTrailFrameOffset * FLAPPY_PIPE_SPEED_PX_PER_FRAME;
534
- let previousYPosition = firstTrailPoint.yPx;
535
- let previousFrameOffset = firstTrailFrameOffset;
536
- const maxTrailFrameOffset = Math.max(1, firstTrailFrameOffset);
537
-
538
- // Step 2: Configure trail stroke style.
539
- const previousGlobalAlpha = context.globalAlpha;
540
- context.strokeStyle = color;
541
- context.lineWidth = FLAPPY_TRAIL_LINE_WIDTH_PX;
542
-
543
- // Step 3: Render stepped segments with edge-proximity alpha fading.
544
- trailPoints.slice(1).forEach((trailPoint) => {
545
- const frameOffset = Math.max(
546
- 0,
547
- latestTrailFrameIndex - trailPoint.frameIndex,
548
- );
549
- const nextXPosition =
550
- anchorX - frameOffset * FLAPPY_PIPE_SPEED_PX_PER_FRAME;
551
- const nextYPosition = trailPoint.yPx;
552
-
553
- const horizontalDeltaPx = nextXPosition - previousXPosition;
554
- const horizontalDirection = Math.sign(horizontalDeltaPx) || 1;
555
- const steppedHorizontalLengthPx = Math.max(
556
- Math.abs(horizontalDeltaPx),
557
- FLAPPY_TRAIL_MIN_HORIZONTAL_SEGMENT_PX,
558
- );
559
- const steppedHorizontalXPosition =
560
- previousXPosition + horizontalDirection * steppedHorizontalLengthPx;
561
- drawTrailSegmentWithEdgeFade(
562
- context,
563
- previousXPosition,
564
- previousYPosition,
565
- steppedHorizontalXPosition,
566
- previousYPosition,
567
- baseOpacity,
568
- edgeBounds,
569
- previousFrameOffset,
570
- frameOffset,
571
- maxTrailFrameOffset,
572
- );
573
- previousXPosition = steppedHorizontalXPosition;
574
-
575
- const verticalDeltaPx = nextYPosition - previousYPosition;
576
- if (verticalDeltaPx !== 0) {
577
- const verticalDirection = Math.sign(verticalDeltaPx);
578
- const steppedVerticalLengthPx = Math.max(
579
- Math.abs(verticalDeltaPx),
580
- FLAPPY_TRAIL_MIN_VERTICAL_SEGMENT_PX,
581
- );
582
- const steppedVerticalYPosition =
583
- previousYPosition + verticalDirection * steppedVerticalLengthPx;
584
- drawTrailSegmentWithEdgeFade(
585
- context,
586
- previousXPosition,
587
- previousYPosition,
588
- steppedHorizontalXPosition,
589
- steppedVerticalYPosition,
590
- baseOpacity,
591
- edgeBounds,
592
- previousFrameOffset,
593
- frameOffset,
594
- maxTrailFrameOffset,
595
- );
596
- previousYPosition = steppedVerticalYPosition;
597
- }
598
-
599
- drawTrailSegmentWithEdgeFade(
600
- context,
601
- previousXPosition,
602
- previousYPosition,
603
- steppedHorizontalXPosition,
604
- nextYPosition,
605
- baseOpacity,
606
- edgeBounds,
607
- previousFrameOffset,
608
- frameOffset,
609
- maxTrailFrameOffset,
610
- );
611
- previousYPosition = nextYPosition;
612
-
613
- drawTrailSegmentWithEdgeFade(
614
- context,
615
- previousXPosition,
616
- previousYPosition,
617
- nextXPosition,
618
- nextYPosition,
619
- baseOpacity,
620
- edgeBounds,
621
- previousFrameOffset,
622
- frameOffset,
623
- maxTrailFrameOffset,
624
- );
625
- previousXPosition = nextXPosition;
626
- previousYPosition = nextYPosition;
627
- previousFrameOffset = frameOffset;
628
- });
629
-
630
- // Step 4: Restore caller alpha state.
631
- context.globalAlpha = previousGlobalAlpha;
632
- }
633
-
634
- /**
635
- * Draws one trail segment with edge-aware alpha attenuation.
636
- *
637
- * @param context - Canvas 2D context.
638
- * @param startXPx - Segment start x position.
639
- * @param startYPx - Segment start y position.
640
- * @param endXPx - Segment end x position.
641
- * @param endYPx - Segment end y position.
642
- * @param baseOpacity - Base trail opacity before edge fading.
643
- * @param edgeBounds - Visible world bounds used for edge distance checks.
644
- * @param startFrameOffset - Age offset at segment start (frames from latest).
645
- * @param endFrameOffset - Age offset at segment end (frames from latest).
646
- * @param maxTrailFrameOffset - Oldest age offset currently retained by the trail.
647
- * @returns Nothing.
648
- */
649
- function drawTrailSegmentWithEdgeFade(
650
- context: CanvasRenderingContext2D,
651
- startXPx: number,
652
- startYPx: number,
653
- endXPx: number,
654
- endYPx: number,
655
- baseOpacity: number,
656
- edgeBounds: PlaybackEdgeBounds,
657
- startFrameOffset: number,
658
- endFrameOffset: number,
659
- maxTrailFrameOffset: number,
660
- ): void {
661
- const segmentLengthPx = Math.hypot(endXPx - startXPx, endYPx - startYPx);
662
- if (segmentLengthPx === 0 || baseOpacity <= 0) {
663
- return;
664
- }
665
-
666
- // Step 1: Resolve edge-fade factor from both segment endpoints.
667
- const startOpacityFactor = resolveEdgeOpacityFactor(
668
- startXPx,
669
- startYPx,
670
- edgeBounds,
671
- );
672
- const endOpacityFactor = resolveEdgeOpacityFactor(endXPx, endYPx, edgeBounds);
673
- const edgeOpacityFactor = Math.min(startOpacityFactor, endOpacityFactor);
674
-
675
- // Step 2: Resolve lifetime fade so older trail history fades near cutoff.
676
- const startLifetimeOpacityFactor = resolveTrailLifetimeOpacityFactor(
677
- startFrameOffset,
678
- maxTrailFrameOffset,
679
- );
680
- const endLifetimeOpacityFactor = resolveTrailLifetimeOpacityFactor(
681
- endFrameOffset,
682
- maxTrailFrameOffset,
683
- );
684
- const lifetimeOpacityFactor = Math.min(
685
- startLifetimeOpacityFactor,
686
- endLifetimeOpacityFactor,
687
- );
688
-
689
- const segmentOpacity =
690
- baseOpacity * edgeOpacityFactor * lifetimeOpacityFactor;
691
- if (segmentOpacity <= 0) {
692
- return;
693
- }
694
-
695
- // Step 3: Draw the segment with resolved opacity.
696
- context.globalAlpha = segmentOpacity;
697
- context.beginPath();
698
- context.moveTo(startXPx, startYPx);
699
- context.lineTo(endXPx, endYPx);
700
- context.stroke();
701
- }
702
-
703
- /**
704
- * Resolves the maximum survived-frame count in the current render state.
705
- *
706
- * @param renderState - Current render state.
707
- * @returns Maximum frames survived.
708
- */
709
- function resolveLeaderFramesSurvived(
710
- renderState: PopulationRenderState,
711
- ): number {
712
- return renderState.birds.reduce(
713
- (maximumFramesSurvived, bird) =>
714
- Math.max(maximumFramesSurvived, bird.framesSurvived),
715
- 0,
716
- );
717
- }
1
+ export { animatePopulationEpisodeInternal } from './playback/playback';