@reicek/neataptic-ts 0.1.21 → 0.1.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/agents/boundary-mapper.agent.md +31 -0
- package/.github/agents/docs-scout.agent.md +29 -0
- package/.github/agents/plan-scout.agent.md +31 -0
- package/.github/agents/solid-split.agent.md +143 -0
- package/.github/copilot-instructions.md +119 -0
- package/.github/skills/solid-split-playbook/SKILL.md +220 -0
- package/.github/skills/solid-split-playbook/assets/docs-checklist.md +34 -0
- package/.github/skills/solid-split-playbook/assets/split-plan-template.md +48 -0
- package/.github/skills/solid-split-playbook/assets/split-workflow-checklist.md +51 -0
- package/.github/skills/trace-analyzer-extension/SKILL.md +63 -0
- package/.github/skills/trace-analyzer-extension/assets/extension-checklist.md +24 -0
- package/.github/skills/trace-analyzer-extension/references/analyzer-extension-workflow.md +101 -0
- package/.github/skills/trace-audit-reporting/SKILL.md +96 -0
- package/.github/skills/trace-audit-reporting/assets/performance-report-template.md +123 -0
- package/.github/skills/trace-audit-reporting/references/trace-analysis-workflow.md +132 -0
- package/package.json +7 -3
- package/plans/ES2023 migration +13 -8
- package/plans/Evolution_Training_Interoperability_Contracts.md +1 -1
- package/plans/Flappy_Bird_Folder_Documentation_Pass.md +53 -0
- package/plans/Flappy_Evolution_Worker_Documentation_Pass.md +58 -0
- package/plans/Interactive_Examples_and_Learning_Path.md +10 -2
- package/plans/Memory_Optimization.md +3 -3
- package/plans/README.md +63 -0
- package/plans/Roadmap.md +15 -3
- package/plans/asciiMaze_SOLID_split.done.md +130 -0
- package/plans/flappy_bird_SOLID_split.done.md +67 -0
- package/scripts/analyze-trace.ts +590 -0
- package/scripts/assets/theme.css +221 -34
- package/scripts/copy-examples.mjs +9 -5
- package/scripts/export-onnx.mjs +3 -3
- package/scripts/generate-bench-tables.mjs +10 -10
- package/scripts/generate-bench-tables.ts +10 -10
- package/scripts/generate-docs.ts +1415 -449
- package/scripts/render-docs-html.ts +15 -8
- package/src/README.md +127 -222
- package/src/architecture/README.md +117 -184
- package/src/architecture/architect.ts +6 -0
- package/src/architecture/layer/README.md +38 -38
- package/src/architecture/network/README.md +49 -31
- package/src/architecture/network/activate/README.md +77 -77
- package/src/architecture/network/connect/README.md +15 -13
- package/src/architecture/network/deterministic/README.md +7 -7
- package/src/architecture/network/evolve/README.md +44 -44
- package/src/architecture/network/gating/README.md +20 -20
- package/src/architecture/network/genetic/README.md +51 -51
- package/src/architecture/network/mutate/README.md +97 -97
- package/src/architecture/network/network.types.ts +39 -0
- package/src/architecture/network/onnx/README.md +264 -264
- package/src/architecture/network/prune/README.md +39 -39
- package/src/architecture/network/remove/README.md +26 -26
- package/src/architecture/network/serialize/README.md +56 -56
- package/src/architecture/network/serialize/network.serialize.json.utils.ts +1 -0
- package/src/architecture/network/serialize/network.serialize.utils.ts +6 -1
- package/src/architecture/network/serialize/network.serialize.utils.types.ts +1 -1
- package/src/architecture/network/slab/README.md +61 -61
- package/src/architecture/network/standalone/README.md +24 -24
- package/src/architecture/network/stats/README.md +9 -9
- package/src/architecture/network/topology/README.md +46 -46
- package/src/architecture/network/training/README.md +21 -21
- package/src/architecture/network.ts +114 -10
- package/src/methods/README.md +9 -87
- package/src/multithreading/README.md +8 -77
- package/src/multithreading/workers/README.md +2 -2
- package/src/multithreading/workers/browser/README.md +0 -6
- package/src/multithreading/workers/node/README.md +0 -3
- package/src/neat/README.md +623 -568
- package/src/neat/neat.evolve.population.utils.ts +29 -5
- package/src/neat/neat.helpers.ts +16 -0
- package/src/neat/neat.topology-intent.utils.ts +160 -0
- package/src/utils/README.md +18 -18
- package/test/examples/asciiMaze/README.md +59 -59
- package/test/examples/asciiMaze/asciiMaze.e2e.test.ts +14 -9
- package/test/examples/asciiMaze/browser-entry/README.md +196 -0
- package/test/examples/asciiMaze/browser-entry/browser-entry.abort.services.ts +95 -0
- package/test/examples/asciiMaze/browser-entry/browser-entry.constants.ts +23 -0
- package/test/examples/asciiMaze/browser-entry/browser-entry.curriculum.services.ts +115 -0
- package/test/examples/asciiMaze/browser-entry/browser-entry.globals.services.ts +106 -0
- package/test/examples/asciiMaze/browser-entry/browser-entry.host.services.ts +157 -0
- package/test/examples/asciiMaze/browser-entry/browser-entry.services.ts +14 -0
- package/test/examples/asciiMaze/browser-entry/browser-entry.ts +129 -0
- package/test/examples/asciiMaze/browser-entry/browser-entry.types.ts +120 -0
- package/test/examples/asciiMaze/browser-entry/browser-entry.utils.ts +98 -0
- package/test/examples/asciiMaze/browser-entry.ts +10 -576
- package/test/examples/asciiMaze/dashboardManager/README.md +276 -0
- package/test/examples/asciiMaze/dashboardManager/archive/README.md +16 -0
- package/test/examples/asciiMaze/dashboardManager/archive/dashboardManager.archive.services.ts +267 -0
- package/test/examples/asciiMaze/dashboardManager/dashboardManager.constants.ts +35 -0
- package/test/examples/asciiMaze/dashboardManager/dashboardManager.services.ts +103 -0
- package/test/examples/asciiMaze/dashboardManager/dashboardManager.ts +181 -0
- package/test/examples/asciiMaze/dashboardManager/dashboardManager.types.ts +267 -0
- package/test/examples/asciiMaze/dashboardManager/dashboardManager.utils.ts +254 -0
- package/test/examples/asciiMaze/dashboardManager/live/README.md +14 -0
- package/test/examples/asciiMaze/dashboardManager/live/dashboardManager.live.services.ts +264 -0
- package/test/examples/asciiMaze/dashboardManager/telemetry/README.md +47 -0
- package/test/examples/asciiMaze/dashboardManager/telemetry/dashboardManager.telemetry.services.ts +513 -0
- package/test/examples/asciiMaze/dashboardManager.ts +13 -2335
- package/test/examples/asciiMaze/evolutionEngine/README.md +1058 -0
- package/test/examples/asciiMaze/evolutionEngine/curriculumPhase.ts +90 -0
- package/test/examples/asciiMaze/evolutionEngine/engineState.constants.ts +36 -0
- package/test/examples/asciiMaze/evolutionEngine/engineState.ts +58 -513
- package/test/examples/asciiMaze/evolutionEngine/engineState.types.ts +212 -0
- package/test/examples/asciiMaze/evolutionEngine/engineState.utils.ts +301 -0
- package/test/examples/asciiMaze/evolutionEngine/evolutionEngine.types.ts +445 -0
- package/test/examples/asciiMaze/evolutionEngine/evolutionLoop.ts +81 -50
- package/test/examples/asciiMaze/evolutionEngine/optionsAndSetup.ts +2 -4
- package/test/examples/asciiMaze/evolutionEngine/populationDynamics.ts +17 -33
- package/test/examples/asciiMaze/evolutionEngine/populationPruning.ts +1 -1
- package/test/examples/asciiMaze/evolutionEngine/rngAndTiming.ts +1 -2
- package/test/examples/asciiMaze/evolutionEngine/sampling.ts +1 -1
- package/test/examples/asciiMaze/evolutionEngine/scratchPools.ts +2 -5
- package/test/examples/asciiMaze/evolutionEngine/setupHelpers.ts +30 -37
- package/test/examples/asciiMaze/evolutionEngine/telemetryMetrics.ts +16 -58
- package/test/examples/asciiMaze/evolutionEngine/trainingWarmStart.ts +2 -2
- package/test/examples/asciiMaze/evolutionEngine.ts +55 -55
- package/test/examples/asciiMaze/fitness.ts +2 -2
- package/test/examples/asciiMaze/fitness.types.ts +65 -0
- package/test/examples/asciiMaze/interfaces.ts +64 -1352
- package/test/examples/asciiMaze/mazeMovement/README.md +356 -0
- package/test/examples/asciiMaze/mazeMovement/finalization/README.md +49 -0
- package/test/examples/asciiMaze/mazeMovement/finalization/mazeMovement.finalization.ts +138 -0
- package/test/examples/asciiMaze/mazeMovement/mazeMovement.constants.ts +101 -0
- package/test/examples/asciiMaze/mazeMovement/mazeMovement.services.ts +230 -0
- package/test/examples/asciiMaze/mazeMovement/mazeMovement.ts +299 -0
- package/test/examples/asciiMaze/mazeMovement/mazeMovement.types.ts +185 -0
- package/test/examples/asciiMaze/mazeMovement/mazeMovement.utils.ts +153 -0
- package/test/examples/asciiMaze/mazeMovement/policy/README.md +91 -0
- package/test/examples/asciiMaze/mazeMovement/policy/mazeMovement.policy.ts +467 -0
- package/test/examples/asciiMaze/mazeMovement/runtime/README.md +95 -0
- package/test/examples/asciiMaze/mazeMovement/runtime/mazeMovement.runtime.ts +354 -0
- package/test/examples/asciiMaze/mazeMovement/shaping/README.md +124 -0
- package/test/examples/asciiMaze/mazeMovement/shaping/mazeMovement.shaping.ts +459 -0
- package/test/examples/asciiMaze/mazeMovement.ts +12 -2978
- package/test/examples/flappy_bird/README.md +193 -88
- package/test/examples/flappy_bird/browser-entry/README.md +1441 -0
- package/test/examples/flappy_bird/browser-entry/browser-entry.host.utils.ts +4 -324
- package/test/examples/flappy_bird/browser-entry/browser-entry.network-view.utils.ts +9 -396
- package/test/examples/flappy_bird/browser-entry/browser-entry.playback.utils.ts +6 -714
- package/test/examples/flappy_bird/browser-entry/browser-entry.render.types.ts +26 -3
- package/test/examples/flappy_bird/browser-entry/browser-entry.runtime.types.ts +16 -1
- package/test/examples/flappy_bird/browser-entry/browser-entry.simulation.types.ts +39 -5
- package/test/examples/flappy_bird/browser-entry/browser-entry.spawn.utils.ts +11 -31
- package/test/examples/flappy_bird/browser-entry/browser-entry.stats.types.ts +32 -4
- package/test/examples/flappy_bird/browser-entry/browser-entry.ts +11 -0
- package/test/examples/flappy_bird/browser-entry/browser-entry.types.ts +8 -0
- package/test/examples/flappy_bird/browser-entry/browser-entry.visualization.types.ts +50 -7
- package/test/examples/flappy_bird/browser-entry/browser-entry.visualization.utils.ts +21 -893
- package/test/examples/flappy_bird/browser-entry/browser-entry.worker.types.ts +91 -10
- package/test/examples/flappy_bird/browser-entry/host/README.md +318 -0
- package/test/examples/flappy_bird/browser-entry/host/host.canvas.service.ts +16 -0
- package/test/examples/flappy_bird/browser-entry/host/host.constants.ts +20 -0
- package/test/examples/flappy_bird/browser-entry/host/host.dom.service.ts +10 -0
- package/test/examples/flappy_bird/browser-entry/host/host.resize.service.ts +1 -295
- package/test/examples/flappy_bird/browser-entry/host/host.stats.service.ts +14 -0
- package/test/examples/flappy_bird/browser-entry/host/host.ts +592 -6
- package/test/examples/flappy_bird/browser-entry/host/host.types.ts +13 -0
- package/test/examples/flappy_bird/browser-entry/host/resize/README.md +309 -0
- package/test/examples/flappy_bird/browser-entry/host/resize/host.resize.service.constants.ts +47 -0
- package/test/examples/flappy_bird/browser-entry/host/resize/host.resize.service.services.ts +392 -0
- package/test/examples/flappy_bird/browser-entry/host/resize/host.resize.service.ts +132 -0
- package/test/examples/flappy_bird/browser-entry/host/resize/host.resize.service.types.ts +92 -0
- package/test/examples/flappy_bird/browser-entry/host/resize/host.resize.service.utils.ts +272 -0
- package/test/examples/flappy_bird/browser-entry/network-view/README.md +389 -0
- package/test/examples/flappy_bird/browser-entry/network-view/network-view.draw.service.ts +13 -0
- package/test/examples/flappy_bird/browser-entry/network-view/network-view.labels.utils.ts +12 -0
- package/test/examples/flappy_bird/browser-entry/network-view/network-view.layout.utils.ts +14 -0
- package/test/examples/flappy_bird/browser-entry/network-view/network-view.topology.utils.ts +267 -0
- package/test/examples/flappy_bird/browser-entry/network-view/network-view.ts +823 -7
- package/test/examples/flappy_bird/browser-entry/network-view/network-view.types.ts +11 -0
- package/test/examples/flappy_bird/browser-entry/playback/README.md +845 -0
- package/test/examples/flappy_bird/browser-entry/playback/background/README.md +355 -0
- package/test/examples/flappy_bird/browser-entry/playback/background/ground-grid/README.md +1068 -0
- package/test/examples/flappy_bird/browser-entry/playback/background/ground-grid/playback.background.ground-grid.batch.services.ts +64 -0
- package/test/examples/flappy_bird/browser-entry/playback/background/ground-grid/playback.background.ground-grid.cache.services.ts +207 -0
- package/test/examples/flappy_bird/browser-entry/playback/background/ground-grid/playback.background.ground-grid.constants.ts +197 -0
- package/test/examples/flappy_bird/browser-entry/playback/background/ground-grid/playback.background.ground-grid.geometry.batch.utils.ts +114 -0
- package/test/examples/flappy_bird/browser-entry/playback/background/ground-grid/playback.background.ground-grid.geometry.layout.utils.test.ts +96 -0
- package/test/examples/flappy_bird/browser-entry/playback/background/ground-grid/playback.background.ground-grid.geometry.layout.utils.ts +204 -0
- package/test/examples/flappy_bird/browser-entry/playback/background/ground-grid/playback.background.ground-grid.geometry.services.ts +49 -0
- package/test/examples/flappy_bird/browser-entry/playback/background/ground-grid/playback.background.ground-grid.geometry.utils.ts +313 -0
- package/test/examples/flappy_bird/browser-entry/playback/background/ground-grid/playback.background.ground-grid.layer.services.ts +81 -0
- package/test/examples/flappy_bird/browser-entry/playback/background/ground-grid/playback.background.ground-grid.math.utils.test.ts +33 -0
- package/test/examples/flappy_bird/browser-entry/playback/background/ground-grid/playback.background.ground-grid.math.utils.ts +201 -0
- package/test/examples/flappy_bird/browser-entry/playback/background/ground-grid/playback.background.ground-grid.pulse.selection.utils.ts +171 -0
- package/test/examples/flappy_bird/browser-entry/playback/background/ground-grid/playback.background.ground-grid.pulse.timing.utils.ts +124 -0
- package/test/examples/flappy_bird/browser-entry/playback/background/ground-grid/playback.background.ground-grid.pulse.utils.test.ts +279 -0
- package/test/examples/flappy_bird/browser-entry/playback/background/ground-grid/playback.background.ground-grid.pulse.utils.ts +132 -0
- package/test/examples/flappy_bird/browser-entry/playback/background/ground-grid/playback.background.ground-grid.scene.services.ts +26 -0
- package/test/examples/flappy_bird/browser-entry/playback/background/ground-grid/playback.background.ground-grid.services.ts +65 -0
- package/test/examples/flappy_bird/browser-entry/playback/background/ground-grid/playback.background.ground-grid.ts +48 -0
- package/test/examples/flappy_bird/browser-entry/playback/background/ground-grid/playback.background.ground-grid.types.ts +342 -0
- package/test/examples/flappy_bird/browser-entry/playback/background/ground-grid/playback.background.ground-grid.utils.ts +10 -0
- package/test/examples/flappy_bird/browser-entry/playback/background/playback.background.cache.services.ts +96 -0
- package/test/examples/flappy_bird/browser-entry/playback/background/playback.background.constants.ts +127 -0
- package/test/examples/flappy_bird/browser-entry/playback/background/playback.background.draw.services.ts +184 -0
- package/test/examples/flappy_bird/browser-entry/playback/background/playback.background.scene.services.ts +64 -0
- package/test/examples/flappy_bird/browser-entry/playback/background/playback.background.services.ts +6 -0
- package/test/examples/flappy_bird/browser-entry/playback/background/playback.background.ts +53 -0
- package/test/examples/flappy_bird/browser-entry/playback/background/playback.background.types.ts +105 -0
- package/test/examples/flappy_bird/browser-entry/playback/background/playback.background.utils.ts +100 -0
- package/test/examples/flappy_bird/browser-entry/playback/frame-render/README.md +541 -0
- package/test/examples/flappy_bird/browser-entry/playback/frame-render/playback.frame-render.bird.utils.ts +180 -0
- package/test/examples/flappy_bird/browser-entry/playback/frame-render/playback.frame-render.canvas.services.ts +77 -0
- package/test/examples/flappy_bird/browser-entry/playback/frame-render/playback.frame-render.entity.services.ts +167 -0
- package/test/examples/flappy_bird/browser-entry/playback/frame-render/playback.frame-render.scene.services.ts +57 -0
- package/test/examples/flappy_bird/browser-entry/playback/frame-render/playback.frame-render.service.test.ts +176 -0
- package/test/examples/flappy_bird/browser-entry/playback/frame-render/playback.frame-render.service.ts +113 -0
- package/test/examples/flappy_bird/browser-entry/playback/frame-render/playback.frame-render.services.ts +35 -0
- package/test/examples/flappy_bird/browser-entry/playback/frame-render/playback.frame-render.trail.utils.ts +248 -0
- package/test/examples/flappy_bird/browser-entry/playback/frame-render/playback.frame-render.types.ts +103 -0
- package/test/examples/flappy_bird/browser-entry/playback/frame-render/playback.frame-render.utils.ts +11 -0
- package/test/examples/flappy_bird/browser-entry/playback/playback.constants.ts +1 -1
- package/test/examples/flappy_bird/browser-entry/playback/playback.frame-render.service.ts +10 -0
- package/test/examples/flappy_bird/browser-entry/playback/playback.iteration.services.ts +192 -0
- package/test/examples/flappy_bird/browser-entry/playback/playback.loop.service.ts +12 -0
- package/test/examples/flappy_bird/browser-entry/playback/playback.orchestration.types.ts +78 -0
- package/test/examples/flappy_bird/browser-entry/playback/playback.render.pipe-outline.service.ts +128 -0
- package/test/examples/flappy_bird/browser-entry/playback/playback.render.service.ts +1 -116
- package/test/examples/flappy_bird/browser-entry/playback/playback.session.services.ts +184 -0
- package/test/examples/flappy_bird/browser-entry/playback/playback.snapshot.utils.test.ts +121 -0
- package/test/examples/flappy_bird/browser-entry/playback/playback.snapshot.utils.ts +8 -0
- package/test/examples/flappy_bird/browser-entry/playback/playback.starfield.layer.services.ts +36 -0
- package/test/examples/flappy_bird/browser-entry/playback/playback.starfield.service.ts +11 -128
- package/test/examples/flappy_bird/browser-entry/playback/playback.starfield.services.ts +268 -0
- package/test/examples/flappy_bird/browser-entry/playback/playback.starfield.types.ts +91 -0
- package/test/examples/flappy_bird/browser-entry/playback/playback.starfield.utils.ts +11 -4
- package/test/examples/flappy_bird/browser-entry/playback/playback.trail.utils.ts +9 -86
- package/test/examples/flappy_bird/browser-entry/playback/playback.ts +75 -7
- package/test/examples/flappy_bird/browser-entry/playback/playback.types.ts +12 -9
- package/test/examples/flappy_bird/browser-entry/playback/playback.worker-channel.utils.ts +11 -123
- package/test/examples/flappy_bird/browser-entry/playback/snapshot/README.md +55 -0
- package/test/examples/flappy_bird/browser-entry/playback/snapshot/playback.snapshot.services.ts +103 -0
- package/test/examples/flappy_bird/browser-entry/playback/snapshot/playback.snapshot.summary.utils.test.ts +45 -0
- package/test/examples/flappy_bird/browser-entry/playback/snapshot/playback.snapshot.summary.utils.ts +28 -0
- package/test/examples/flappy_bird/browser-entry/playback/trail/README.md +95 -0
- package/test/examples/flappy_bird/browser-entry/playback/trail/playback.trail.history.services.test.ts +35 -0
- package/test/examples/flappy_bird/browser-entry/playback/trail/playback.trail.history.services.ts +64 -0
- package/test/examples/flappy_bird/browser-entry/playback/trail/playback.trail.opacity.utils.test.ts +37 -0
- package/test/examples/flappy_bird/browser-entry/playback/trail/playback.trail.opacity.utils.ts +74 -0
- package/test/examples/flappy_bird/browser-entry/playback/worker-channel/README.md +71 -0
- package/test/examples/flappy_bird/browser-entry/playback/worker-channel/playback.worker-channel.request.services.ts +45 -0
- package/test/examples/flappy_bird/browser-entry/playback/worker-channel/playback.worker-channel.summary.services.ts +74 -0
- package/test/examples/flappy_bird/browser-entry/playback/worker-channel/playback.worker-channel.types.ts +53 -0
- package/test/examples/flappy_bird/browser-entry/runtime/README.md +304 -0
- package/test/examples/flappy_bird/browser-entry/runtime/runtime.browser-globals.service.ts +15 -0
- package/test/examples/flappy_bird/browser-entry/runtime/runtime.errors.ts +17 -0
- package/test/examples/flappy_bird/browser-entry/runtime/runtime.evolution-launch.service.ts +56 -0
- package/test/examples/flappy_bird/browser-entry/runtime/runtime.evolution-loop.service.ts +19 -0
- package/test/examples/flappy_bird/browser-entry/runtime/runtime.lifecycle.service.ts +96 -0
- package/test/examples/flappy_bird/browser-entry/runtime/runtime.startup.service.ts +92 -0
- package/test/examples/flappy_bird/browser-entry/runtime/runtime.telemetry.service.ts +24 -0
- package/test/examples/flappy_bird/browser-entry/runtime/runtime.ts +31 -121
- package/test/examples/flappy_bird/browser-entry/runtime/runtime.types.ts +65 -0
- package/test/examples/flappy_bird/browser-entry/visualization/README.md +568 -0
- package/test/examples/flappy_bird/browser-entry/visualization/visualization.colors.utils.ts +26 -0
- package/test/examples/flappy_bird/browser-entry/visualization/visualization.constants.ts +110 -0
- package/test/examples/flappy_bird/browser-entry/visualization/visualization.draw.service.ts +979 -19
- package/test/examples/flappy_bird/browser-entry/visualization/visualization.legend.utils.ts +157 -3
- package/test/examples/flappy_bird/browser-entry/visualization/visualization.topology.utils.ts +13 -27
- package/test/examples/flappy_bird/browser-entry/visualization/visualization.ts +7 -20
- package/test/examples/flappy_bird/browser-entry/visualization/visualization.types.ts +14 -0
- package/test/examples/flappy_bird/browser-entry/worker-channel/README.md +238 -0
- package/test/examples/flappy_bird/browser-entry/worker-channel/worker-channel.errors.ts +11 -0
- package/test/examples/flappy_bird/browser-entry/worker-channel/worker-channel.generation.service.ts +12 -0
- package/test/examples/flappy_bird/browser-entry/worker-channel/worker-channel.playback.service.test.ts +143 -0
- package/test/examples/flappy_bird/browser-entry/worker-channel/worker-channel.playback.service.ts +140 -14
- package/test/examples/flappy_bird/browser-entry/worker-channel/worker-channel.request.service.ts +27 -0
- package/test/examples/flappy_bird/browser-entry/worker-channel/worker-channel.ts +8 -0
- package/test/examples/flappy_bird/browser-entry/worker-channel/worker-channel.types.ts +23 -0
- package/test/examples/flappy_bird/browser-entry/worker-channel/worker-channel.url.service.ts +5 -0
- package/test/examples/flappy_bird/constants/README.md +1163 -0
- package/test/examples/flappy_bird/constants/constants.birds.ts +16 -38
- package/test/examples/flappy_bird/constants/constants.difficulty.ts +21 -0
- package/test/examples/flappy_bird/constants/constants.network-view.ts +24 -0
- package/test/examples/flappy_bird/constants/constants.network.ts +1 -1
- package/test/examples/flappy_bird/constants/constants.observation.ts +7 -0
- package/test/examples/flappy_bird/constants/constants.palette.ts +9 -2
- package/test/examples/flappy_bird/constants/constants.physics.ts +9 -0
- package/test/examples/flappy_bird/constants/constants.pipe-render.ts +3 -0
- package/test/examples/flappy_bird/constants/constants.pipes.ts +22 -3
- package/test/examples/flappy_bird/constants/constants.runtime.ts +28 -4
- package/test/examples/flappy_bird/constants/constants.starfield.ts +78 -3
- package/test/examples/flappy_bird/constants/constants.ts +6 -0
- package/test/examples/flappy_bird/environment/README.md +182 -0
- package/test/examples/flappy_bird/environment/environment.collision.utils.ts +7 -0
- package/test/examples/flappy_bird/environment/environment.constants.ts +16 -3
- package/test/examples/flappy_bird/environment/environment.observation.utils.ts +12 -19
- package/test/examples/flappy_bird/environment/environment.state.service.ts +10 -0
- package/test/examples/flappy_bird/environment/environment.step.service.ts +15 -66
- package/test/examples/flappy_bird/environment/environment.types.ts +14 -0
- package/test/examples/flappy_bird/evaluation/README.md +155 -0
- package/test/examples/flappy_bird/evaluation/evaluation.constants.ts +23 -4
- package/test/examples/flappy_bird/evaluation/evaluation.fitness.utils.ts +16 -1
- package/test/examples/flappy_bird/evaluation/evaluation.rollout.service.ts +7 -374
- package/test/examples/flappy_bird/evaluation/evaluation.seed.utils.ts +4 -0
- package/test/examples/flappy_bird/evaluation/evaluation.types.ts +18 -2
- package/test/examples/flappy_bird/evaluation/rollout/README.md +355 -0
- package/test/examples/flappy_bird/evaluation/rollout/evaluation.rollout.constants.ts +38 -0
- package/test/examples/flappy_bird/evaluation/rollout/evaluation.rollout.service.ts +71 -0
- package/test/examples/flappy_bird/evaluation/rollout/evaluation.rollout.services.ts +338 -0
- package/test/examples/flappy_bird/evaluation/rollout/evaluation.rollout.types.ts +69 -0
- package/test/examples/flappy_bird/evaluation/rollout/evaluation.rollout.utils.ts +399 -0
- package/test/examples/flappy_bird/flappy-evolution-worker/README.md +845 -0
- package/test/examples/flappy_bird/flappy-evolution-worker/flappy-evolution-worker.constants.ts +49 -7
- package/test/examples/flappy_bird/flappy-evolution-worker/flappy-evolution-worker.errors.ts +34 -3
- package/test/examples/flappy_bird/flappy-evolution-worker/flappy-evolution-worker.evolution.service.ts +22 -0
- package/test/examples/flappy_bird/flappy-evolution-worker/flappy-evolution-worker.playback.service.ts +62 -26
- package/test/examples/flappy_bird/flappy-evolution-worker/flappy-evolution-worker.protocol.service.ts +27 -1
- package/test/examples/flappy_bird/flappy-evolution-worker/flappy-evolution-worker.runtime.service.ts +23 -0
- package/test/examples/flappy_bird/flappy-evolution-worker/flappy-evolution-worker.simulation.frame.service.ts +378 -0
- package/test/examples/flappy_bird/flappy-evolution-worker/flappy-evolution-worker.simulation.types.ts +22 -0
- package/test/examples/flappy_bird/flappy-evolution-worker/flappy-evolution-worker.simulation.utils.ts +20 -203
- package/test/examples/flappy_bird/flappy-evolution-worker/flappy-evolution-worker.snapshot.utils.test.ts +94 -0
- package/test/examples/flappy_bird/flappy-evolution-worker/flappy-evolution-worker.snapshot.utils.ts +78 -13
- package/test/examples/flappy_bird/flappy-evolution-worker/flappy-evolution-worker.ts +235 -344
- package/test/examples/flappy_bird/flappy-evolution-worker/flappy-evolution-worker.types.ts +170 -22
- package/test/examples/flappy_bird/flappy-evolution-worker/flappy-evolution-worker.warm-start.service.ts +314 -0
- package/test/examples/flappy_bird/flappy.simulation.shared.utils.ts +17 -0
- package/test/examples/flappy_bird/flappyEnvironment.ts +21 -0
- package/test/examples/flappy_bird/flappyEvaluation.ts +12 -0
- package/test/examples/flappy_bird/flappyEvolution.worker.ts +7 -0
- package/test/examples/flappy_bird/index.ts +8 -2
- package/test/examples/flappy_bird/rng.ts +10 -0
- package/test/examples/flappy_bird/simulation-shared/README.md +518 -0
- package/test/examples/flappy_bird/simulation-shared/observation/README.md +255 -0
- package/test/examples/flappy_bird/simulation-shared/observation/observation.features.utils.ts +339 -0
- package/test/examples/flappy_bird/simulation-shared/observation/observation.ts +19 -0
- package/test/examples/flappy_bird/simulation-shared/observation/observation.vector.utils.ts +81 -0
- package/test/examples/flappy_bird/simulation-shared/simulation-shared.constants.ts +3 -0
- package/test/examples/flappy_bird/simulation-shared/simulation-shared.control.utils.ts +6 -0
- package/test/examples/flappy_bird/simulation-shared/simulation-shared.difficulty.utils.ts +9 -0
- package/test/examples/flappy_bird/simulation-shared/simulation-shared.errors.ts +10 -1
- package/test/examples/flappy_bird/simulation-shared/simulation-shared.memory.utils.ts +18 -0
- package/test/examples/flappy_bird/simulation-shared/simulation-shared.observation.utils.ts +7 -402
- package/test/examples/flappy_bird/simulation-shared/simulation-shared.spawn.utils.ts +36 -6
- package/test/examples/flappy_bird/{evaluation/evaluation.statistics.utils.ts → simulation-shared/simulation-shared.statistics.utils.ts} +30 -9
- package/test/examples/flappy_bird/simulation-shared/simulation-shared.types.ts +38 -5
- package/test/examples/flappy_bird/trainFlappyBird.ts +13 -0
- package/test/examples/flappy_bird/trainer/README.md +676 -0
- package/test/examples/flappy_bird/trainer/evaluation/README.md +253 -0
- package/test/examples/flappy_bird/trainer/evaluation/trainer.evaluation.service.constants.ts +15 -0
- package/test/examples/flappy_bird/trainer/evaluation/trainer.evaluation.service.services.ts +86 -0
- package/test/examples/flappy_bird/trainer/evaluation/trainer.evaluation.service.ts +187 -0
- package/test/examples/flappy_bird/trainer/evaluation/trainer.evaluation.service.types.ts +32 -0
- package/test/examples/flappy_bird/trainer/evaluation/trainer.evaluation.service.utils.ts +182 -0
- package/test/examples/flappy_bird/trainer/trainer.evaluation.service.ts +13 -0
- package/test/examples/flappy_bird/trainer/trainer.fitness.service.ts +23 -1
- package/test/examples/flappy_bird/trainer/trainer.loop.service.ts +17 -1
- package/test/examples/flappy_bird/trainer/trainer.report.service.services.ts +181 -0
- package/test/examples/flappy_bird/trainer/trainer.report.service.ts +136 -0
- package/test/examples/flappy_bird/trainer/trainer.selection.utils.ts +89 -0
- package/test/examples/flappy_bird/trainer/trainer.setup.service.ts +22 -0
- package/test/examples/flappy_bird/trainer/trainer.signals.service.ts +8 -0
- package/test/examples/flappy_bird/trainer/trainer.ts +38 -553
- package/test/examples/flappy_bird/trainer/trainer.types.ts +44 -7
- package/test/neat/neat.topology-intent.test.ts +129 -0
- package/test/network/network.topology-intent.test.ts +44 -0
- package/test/examples/flappy_bird/browser-entry/browser-entry.utils.ts +0 -12
- package/test/examples/flappy_bird/environment/environment.ts +0 -7
- package/test/examples/flappy_bird/evaluation/evaluation.ts +0 -7
- package/test/examples/flappy_bird/simulation-shared/simulation-shared.ts +0 -15
- package/test/examples/flappy_bird/trainer/trainer.statistics.utils.ts +0 -78
|
@@ -1,21 +1,146 @@
|
|
|
1
|
+
import {
|
|
2
|
+
FLAPPY_MONOSPACE_FONT_FAMILY,
|
|
3
|
+
FLAPPY_NEON_PALETTE,
|
|
4
|
+
FLAPPY_NETWORK_HEADER_FONT_SIZE_PX,
|
|
5
|
+
FLAPPY_NETWORK_HEADER_TEXT_COLOR,
|
|
6
|
+
FLAPPY_NETWORK_HIDDEN_NODE_STROKE_COLOR,
|
|
7
|
+
FLAPPY_NETWORK_LEGEND_BACKGROUND,
|
|
8
|
+
FLAPPY_NETWORK_LEGEND_BIAS_TITLE_COLOR,
|
|
9
|
+
FLAPPY_NETWORK_LEGEND_COMPACT_FONT_SIZE_PX,
|
|
10
|
+
FLAPPY_NETWORK_LEGEND_CONNECTION_LINE_WIDTH_PX,
|
|
11
|
+
FLAPPY_NETWORK_LEGEND_CONNECTION_TITLE_COLOR,
|
|
12
|
+
FLAPPY_NETWORK_LEGEND_HEADER_COLOR,
|
|
13
|
+
FLAPPY_NETWORK_LEGEND_REGULAR_FONT_SIZE_PX,
|
|
14
|
+
FLAPPY_NETWORK_LEGEND_ROW_TEXT_COLOR,
|
|
15
|
+
FLAPPY_NETWORK_MIN_LABEL_HEIGHT_PX,
|
|
16
|
+
FLAPPY_NETWORK_NODE_LABEL_FILL_COLOR,
|
|
17
|
+
FLAPPY_NETWORK_NODE_LABEL_FONT_WEIGHT,
|
|
18
|
+
FLAPPY_NETWORK_NODE_LABEL_SIZE_RATIO,
|
|
19
|
+
FLAPPY_NETWORK_OUTPUT_NODE_GLOW_COLOR,
|
|
20
|
+
FLAPPY_NETWORK_OUTPUT_NODE_STROKE_COLOR,
|
|
21
|
+
FLAPPY_VIEWPORT_NETWORK_OVERLAY_HIDDEN_BREAKPOINT_PX,
|
|
22
|
+
} from '../../constants/constants';
|
|
23
|
+
import { applyAlphaToHexColor, clamp } from '../browser-entry.math.utils';
|
|
1
24
|
import type {
|
|
25
|
+
ColorLegendRow,
|
|
26
|
+
NetworkNodeDimensionsLike,
|
|
27
|
+
NetworkLegendLayout,
|
|
2
28
|
PositionedNetworkNodeLike,
|
|
3
29
|
VisualNetworkConnectionLike,
|
|
4
|
-
NetworkNodeDimensionsLike,
|
|
5
30
|
} from '../browser-entry.types';
|
|
31
|
+
import {
|
|
32
|
+
FLAPPY_NETWORK_DOTTED_CONNECTION_ALIGNMENT_EPSILON,
|
|
33
|
+
FLAPPY_NETWORK_DISABLED_CONNECTION_ALPHA,
|
|
34
|
+
FLAPPY_NETWORK_DISABLED_CONNECTION_DASH_PATTERN,
|
|
35
|
+
FLAPPY_NETWORK_DOTTED_CONNECTION_SQUARE_SIDE_PX,
|
|
36
|
+
FLAPPY_NETWORK_DOTTED_CONNECTION_STEP_COMPACT_RATIO,
|
|
37
|
+
FLAPPY_NETWORK_DOTTED_CONNECTION_WIDTH_SPACING_RATIO,
|
|
38
|
+
FLAPPY_NETWORK_ENABLED_CONNECTION_ALPHA,
|
|
39
|
+
FLAPPY_NETWORK_HEADER_LINE_HEIGHT_PX,
|
|
40
|
+
FLAPPY_NETWORK_HEADER_PADDING_PX,
|
|
41
|
+
FLAPPY_NETWORK_HIDDEN_NODE_STROKE_WIDTH_PX,
|
|
42
|
+
FLAPPY_NETWORK_HIDDEN_NODE_VERTICAL_PADDING_PX,
|
|
43
|
+
FLAPPY_NETWORK_LEGEND_ARCHITECTURE_GAP_PX,
|
|
44
|
+
FLAPPY_NETWORK_LEGEND_BIAS_LABEL_X_PX,
|
|
45
|
+
FLAPPY_NETWORK_LEGEND_BIAS_SWATCH_SIZE_PX,
|
|
46
|
+
FLAPPY_NETWORK_LEGEND_BIAS_SWATCH_X_PX,
|
|
47
|
+
FLAPPY_NETWORK_LEGEND_BIAS_SWATCH_Y_PX,
|
|
48
|
+
FLAPPY_NETWORK_LEGEND_BOX_PADDING_PX,
|
|
49
|
+
FLAPPY_NETWORK_LEGEND_CONNECTION_LABEL_X_PX,
|
|
50
|
+
FLAPPY_NETWORK_LEGEND_CONNECTION_SAMPLE_END_X_PX,
|
|
51
|
+
FLAPPY_NETWORK_LEGEND_CONNECTION_SAMPLE_Y_OFFSET_PX,
|
|
52
|
+
FLAPPY_NETWORK_LEGEND_HEADER_TOP_PADDING_PX,
|
|
53
|
+
FLAPPY_NETWORK_LEGEND_MIN_ARCHITECTURE_TOP_PX,
|
|
54
|
+
FLAPPY_NETWORK_MIN_RENDER_NODE_HEIGHT_PX,
|
|
55
|
+
FLAPPY_NETWORK_OUTPUT_NODE_HEIGHT_REDUCTION_PX,
|
|
56
|
+
FLAPPY_NETWORK_OUTPUT_NODE_SHADOW_BLUR_PX,
|
|
57
|
+
FLAPPY_NETWORK_OUTPUT_NODE_STROKE_WIDTH_PX,
|
|
58
|
+
} from './visualization.constants';
|
|
59
|
+
import {
|
|
60
|
+
createColorLegendRows,
|
|
61
|
+
resolveNetworkLegendLayout,
|
|
62
|
+
} from './visualization.legend.utils';
|
|
63
|
+
import { formatNodeBiasLabel } from './visualization.topology.utils';
|
|
6
64
|
import type {
|
|
7
65
|
DynamicColorScale,
|
|
8
66
|
NetworkVisualizationColorScales,
|
|
9
67
|
} from './visualization.types';
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
68
|
+
import { resolveTierColor } from './visualization.colors.utils';
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Canvas drawing helpers for the network visualization panel.
|
|
72
|
+
*
|
|
73
|
+
* This module turns prepared topology, color scales, and legend layout into the
|
|
74
|
+
* actual rendered network view. The emphasis is educational readability: clear
|
|
75
|
+
* topology, readable bias labels, and a legend that explains the neon encoding.
|
|
76
|
+
*/
|
|
77
|
+
|
|
78
|
+
const FLAPPY_MULTILINE_LABEL_SEPARATOR = '\n';
|
|
79
|
+
const FLAPPY_CANVAS_TEXT_ALIGN_LEFT: CanvasTextAlign = 'left';
|
|
80
|
+
const FLAPPY_CANVAS_TEXT_ALIGN_CENTER: CanvasTextAlign = 'center';
|
|
81
|
+
const FLAPPY_CANVAS_TEXT_BASELINE_TOP: CanvasTextBaseline = 'top';
|
|
82
|
+
const FLAPPY_CANVAS_TEXT_BASELINE_ALPHABETIC: CanvasTextBaseline = 'alphabetic';
|
|
83
|
+
const FLAPPY_TRANSPARENT_CANVAS_COLOR = 'transparent';
|
|
84
|
+
const FLAPPY_NETWORK_OUTPUT_NODE_TYPE = 'output';
|
|
85
|
+
const FLAPPY_NETWORK_LEGEND_TITLE = 'Legend';
|
|
86
|
+
const FLAPPY_NETWORK_CONNECTION_SECTION_TITLE = 'Connection weight';
|
|
87
|
+
const FLAPPY_NETWORK_BIAS_SECTION_TITLE = 'Node bias';
|
|
88
|
+
const FLAPPY_CONNECTION_LEGEND_SYMBOL = 'w';
|
|
89
|
+
const FLAPPY_BIAS_LEGEND_SYMBOL = 'b';
|
|
90
|
+
|
|
91
|
+
type WeightedConnectionScene = {
|
|
92
|
+
fromPosition: PositionedNetworkNodeLike;
|
|
93
|
+
toPosition: PositionedNetworkNodeLike;
|
|
94
|
+
connectionColor: string;
|
|
95
|
+
dashPattern: number[];
|
|
96
|
+
isNegativeConnection: boolean;
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
type BiasNodeLabelMetrics = {
|
|
100
|
+
labelAscentPx: number;
|
|
101
|
+
labelDescentPx: number;
|
|
102
|
+
measuredLabelHeightPx: number;
|
|
103
|
+
labelFont: string;
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
type BiasNodePaintStyle = {
|
|
107
|
+
isOutputNode: boolean;
|
|
108
|
+
nodeFillColor: string;
|
|
109
|
+
nodeStrokeColor: string;
|
|
110
|
+
nodeStrokeWidthPx: number;
|
|
111
|
+
nodeShadowBlurPx: number;
|
|
112
|
+
nodeShadowColor: string;
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
type BiasNodeScene = BiasNodePaintStyle & {
|
|
116
|
+
positionedNode: PositionedNetworkNodeLike;
|
|
117
|
+
nodeLabel: string;
|
|
118
|
+
nodeRectLeftPx: number;
|
|
119
|
+
nodeRectTopPx: number;
|
|
120
|
+
resolvedNodeHeightPx: number;
|
|
121
|
+
labelBaselineYPx: number;
|
|
122
|
+
labelFont: string;
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
type LegendSceneContext = NetworkLegendLayout & {
|
|
126
|
+
architectureLines: string[];
|
|
127
|
+
architectureTextTopPx: number;
|
|
128
|
+
connectionLegendRows: ColorLegendRow[];
|
|
129
|
+
biasLegendRows: ColorLegendRow[];
|
|
130
|
+
};
|
|
16
131
|
|
|
17
132
|
/**
|
|
18
133
|
* Draws weighted connection lines.
|
|
134
|
+
*
|
|
135
|
+
* Connection styling carries semantic meaning: color encodes magnitude and sign,
|
|
136
|
+
* while dash patterns and auxiliary marks help distinguish disabled or negative
|
|
137
|
+
* edges in a way that still reads quickly on a dense graph.
|
|
138
|
+
*
|
|
139
|
+
* @param context - Render context.
|
|
140
|
+
* @param runtimeConnections - Runtime connection list.
|
|
141
|
+
* @param positionByNodeIndex - Node layout map.
|
|
142
|
+
* @param connectionScale - Dynamic connection color scale.
|
|
143
|
+
* @returns Nothing.
|
|
19
144
|
*/
|
|
20
145
|
export function drawWeightedConnectionsLayer(
|
|
21
146
|
context: CanvasRenderingContext2D,
|
|
@@ -23,16 +148,38 @@ export function drawWeightedConnectionsLayer(
|
|
|
23
148
|
positionByNodeIndex: Map<number, PositionedNetworkNodeLike>,
|
|
24
149
|
connectionScale: DynamicColorScale,
|
|
25
150
|
): void {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
151
|
+
// Step 1: Resolve the subset of connections that can be drawn from the layout map.
|
|
152
|
+
const drawableConnections = runtimeConnections.flatMap(
|
|
153
|
+
(runtimeConnection) => {
|
|
154
|
+
const weightedConnectionScene = resolveWeightedConnectionScene(
|
|
155
|
+
runtimeConnection,
|
|
156
|
+
positionByNodeIndex,
|
|
157
|
+
connectionScale,
|
|
158
|
+
);
|
|
159
|
+
return weightedConnectionScene ? [weightedConnectionScene] : [];
|
|
160
|
+
},
|
|
31
161
|
);
|
|
162
|
+
|
|
163
|
+
// Step 2: Paint each connection using the resolved scene attributes.
|
|
164
|
+
drawableConnections.forEach((weightedConnectionScene) => {
|
|
165
|
+
drawWeightedConnectionScene(context, weightedConnectionScene);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
// Step 3: Reset the dash pattern so later layers inherit a clean canvas state.
|
|
169
|
+
context.setLineDash([]);
|
|
32
170
|
}
|
|
33
171
|
|
|
34
172
|
/**
|
|
35
173
|
* Draws all network nodes with bias labels.
|
|
174
|
+
*
|
|
175
|
+
* The node layer pairs each rectangle with a compact bias label so the panel can
|
|
176
|
+
* show both topology and a lightweight hint of parameter state.
|
|
177
|
+
*
|
|
178
|
+
* @param context - Render context.
|
|
179
|
+
* @param positionedNodes - Positioned nodes.
|
|
180
|
+
* @param nodeDimensions - Node dimensions.
|
|
181
|
+
* @param biasScale - Dynamic bias color scale.
|
|
182
|
+
* @returns Nothing.
|
|
36
183
|
*/
|
|
37
184
|
export function drawBiasNodesLayer(
|
|
38
185
|
context: CanvasRenderingContext2D,
|
|
@@ -40,31 +187,844 @@ export function drawBiasNodesLayer(
|
|
|
40
187
|
nodeDimensions: NetworkNodeDimensionsLike,
|
|
41
188
|
biasScale: DynamicColorScale,
|
|
42
189
|
): void {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
190
|
+
const halfNodeWidthPx = nodeDimensions.widthPx * 0.5;
|
|
191
|
+
|
|
192
|
+
// Step 1: Resolve each node into a paint-ready scene with stable label metrics.
|
|
193
|
+
const biasNodeScenes = positionedNodes.map((positionedNode) =>
|
|
194
|
+
resolveBiasNodeScene(
|
|
195
|
+
context,
|
|
196
|
+
positionedNode,
|
|
197
|
+
nodeDimensions,
|
|
198
|
+
halfNodeWidthPx,
|
|
199
|
+
biasScale,
|
|
200
|
+
),
|
|
48
201
|
);
|
|
202
|
+
|
|
203
|
+
// Step 2: Draw each node rectangle and optional bias label.
|
|
204
|
+
biasNodeScenes.forEach((biasNodeScene) => {
|
|
205
|
+
drawBiasNodeScene(context, biasNodeScene, nodeDimensions.widthPx);
|
|
206
|
+
});
|
|
49
207
|
}
|
|
50
208
|
|
|
51
209
|
/**
|
|
52
210
|
* Draws network architecture header text.
|
|
211
|
+
*
|
|
212
|
+
* The header gives viewers a compact architecture summary before they inspect
|
|
213
|
+
* individual nodes and edges.
|
|
214
|
+
*
|
|
215
|
+
* @param context - Render context.
|
|
216
|
+
* @param architectureLabel - Header label.
|
|
217
|
+
* @returns Nothing.
|
|
53
218
|
*/
|
|
54
219
|
export function drawNetworkVisualizationHeader(
|
|
55
220
|
context: CanvasRenderingContext2D,
|
|
56
221
|
architectureLabel: string,
|
|
57
222
|
): void {
|
|
58
|
-
|
|
223
|
+
// Step 1: Normalize the potentially multiline architecture label into rows.
|
|
224
|
+
const headerLines = architectureLabel.split(FLAPPY_MULTILINE_LABEL_SEPARATOR);
|
|
225
|
+
|
|
226
|
+
// Step 2: Paint the header block using the shared left-aligned text helper.
|
|
227
|
+
drawLeftAlignedTextRows(context, {
|
|
228
|
+
lines: headerLines,
|
|
229
|
+
leftPx: FLAPPY_NETWORK_HEADER_PADDING_PX,
|
|
230
|
+
topPx: FLAPPY_NETWORK_HEADER_PADDING_PX,
|
|
231
|
+
lineHeightPx: FLAPPY_NETWORK_HEADER_LINE_HEIGHT_PX,
|
|
232
|
+
font: `${FLAPPY_NETWORK_HEADER_FONT_SIZE_PX}px ${FLAPPY_MONOSPACE_FONT_FAMILY}`,
|
|
233
|
+
fillStyle: FLAPPY_NETWORK_HEADER_TEXT_COLOR,
|
|
234
|
+
});
|
|
59
235
|
}
|
|
60
236
|
|
|
61
237
|
/**
|
|
62
238
|
* Draws the color legend for connections and node bias values.
|
|
239
|
+
*
|
|
240
|
+
* This legend is what turns the panel from "colorful art" into an interpretable
|
|
241
|
+
* instrument: it tells the viewer what each weight and bias color actually
|
|
242
|
+
* means numerically.
|
|
243
|
+
*
|
|
244
|
+
* @param context - Render context.
|
|
245
|
+
* @param architectureLabel - Compact architecture description.
|
|
246
|
+
* @param colorScales - Connection and bias color scales.
|
|
247
|
+
* @returns Nothing.
|
|
63
248
|
*/
|
|
64
249
|
export function drawNetworkColorLegend(
|
|
65
250
|
context: CanvasRenderingContext2D,
|
|
66
251
|
architectureLabel: string,
|
|
67
252
|
colorScales: NetworkVisualizationColorScales,
|
|
68
253
|
): void {
|
|
69
|
-
|
|
254
|
+
// Step 1: Skip legend work entirely when the viewport intentionally hides overlays.
|
|
255
|
+
if (shouldHideNetworkColorLegend(context)) {
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Step 2: Resolve legend rows, layout, and derived text bounds once up front.
|
|
260
|
+
const legendSceneContext = resolveLegendSceneContext(
|
|
261
|
+
context,
|
|
262
|
+
architectureLabel,
|
|
263
|
+
colorScales,
|
|
264
|
+
);
|
|
265
|
+
|
|
266
|
+
// Step 3: Paint the architecture label that sits above the legend box.
|
|
267
|
+
drawLegendArchitectureLabel(context, legendSceneContext);
|
|
268
|
+
|
|
269
|
+
// Step 4: Paint the legend container and section header.
|
|
270
|
+
drawLegendFrame(context, legendSceneContext);
|
|
271
|
+
drawLegendHeader(context, legendSceneContext);
|
|
272
|
+
|
|
273
|
+
// Step 5: Paint the connection and bias sections using the resolved rows.
|
|
274
|
+
drawLegendConnectionSection(context, legendSceneContext);
|
|
275
|
+
drawLegendBiasSection(context, legendSceneContext);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Resolves a renderable connection scene from runtime data and node positions.
|
|
280
|
+
*
|
|
281
|
+
* @param runtimeConnection - Candidate runtime connection.
|
|
282
|
+
* @param positionByNodeIndex - Node layout map.
|
|
283
|
+
* @param connectionScale - Connection color scale.
|
|
284
|
+
* @returns Renderable connection scene, when both endpoint nodes exist.
|
|
285
|
+
*/
|
|
286
|
+
function resolveWeightedConnectionScene(
|
|
287
|
+
runtimeConnection: VisualNetworkConnectionLike,
|
|
288
|
+
positionByNodeIndex: Map<number, PositionedNetworkNodeLike>,
|
|
289
|
+
connectionScale: DynamicColorScale,
|
|
290
|
+
): WeightedConnectionScene | undefined {
|
|
291
|
+
// Step 1: Resolve both endpoint indices and exit early when either side is missing.
|
|
292
|
+
const fromNodeIndex = runtimeConnection.from?.index;
|
|
293
|
+
const toNodeIndex = runtimeConnection.to?.index;
|
|
294
|
+
if (typeof fromNodeIndex !== 'number' || typeof toNodeIndex !== 'number') {
|
|
295
|
+
return undefined;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// Step 2: Resolve endpoint coordinates from the current node layout map.
|
|
299
|
+
const fromPosition = positionByNodeIndex.get(fromNodeIndex);
|
|
300
|
+
const toPosition = positionByNodeIndex.get(toNodeIndex);
|
|
301
|
+
if (!fromPosition || !toPosition) {
|
|
302
|
+
return undefined;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// Step 3: Convert weight semantics into color, alpha, and dash styling.
|
|
306
|
+
const connectionWeight = runtimeConnection.weight ?? 0;
|
|
307
|
+
const connectionEnabled = runtimeConnection.enabled !== false;
|
|
308
|
+
const baseColor = resolveTierColor(
|
|
309
|
+
connectionWeight,
|
|
310
|
+
connectionScale.tiers,
|
|
311
|
+
connectionScale.aboveTierColor,
|
|
312
|
+
);
|
|
313
|
+
const connectionAlpha = connectionEnabled
|
|
314
|
+
? FLAPPY_NETWORK_ENABLED_CONNECTION_ALPHA
|
|
315
|
+
: FLAPPY_NETWORK_DISABLED_CONNECTION_ALPHA;
|
|
316
|
+
|
|
317
|
+
return {
|
|
318
|
+
fromPosition,
|
|
319
|
+
toPosition,
|
|
320
|
+
connectionColor: applyAlphaToHexColor(baseColor, connectionAlpha),
|
|
321
|
+
dashPattern: connectionEnabled
|
|
322
|
+
? []
|
|
323
|
+
: [...FLAPPY_NETWORK_DISABLED_CONNECTION_DASH_PATTERN],
|
|
324
|
+
isNegativeConnection: connectionWeight < 0,
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* Draws a previously resolved weighted connection scene.
|
|
330
|
+
*
|
|
331
|
+
* @param context - Render context.
|
|
332
|
+
* @param weightedConnectionScene - Render-ready connection scene.
|
|
333
|
+
* @returns Nothing.
|
|
334
|
+
*/
|
|
335
|
+
function drawWeightedConnectionScene(
|
|
336
|
+
context: CanvasRenderingContext2D,
|
|
337
|
+
weightedConnectionScene: WeightedConnectionScene,
|
|
338
|
+
): void {
|
|
339
|
+
// Step 1: Use square-dot rendering for negative weights so polarity stays visually distinct.
|
|
340
|
+
if (weightedConnectionScene.isNegativeConnection) {
|
|
341
|
+
drawSquareDottedConnection(context, {
|
|
342
|
+
fromXPx: weightedConnectionScene.fromPosition.xPx,
|
|
343
|
+
fromYPx: weightedConnectionScene.fromPosition.yPx,
|
|
344
|
+
toXPx: weightedConnectionScene.toPosition.xPx,
|
|
345
|
+
toYPx: weightedConnectionScene.toPosition.yPx,
|
|
346
|
+
color: weightedConnectionScene.connectionColor,
|
|
347
|
+
lineWidthPx: FLAPPY_NETWORK_LEGEND_CONNECTION_LINE_WIDTH_PX,
|
|
348
|
+
});
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// Step 2: Paint positive-weight links as direct strokes with optional disabled dashes.
|
|
353
|
+
context.strokeStyle = weightedConnectionScene.connectionColor;
|
|
354
|
+
context.lineWidth = FLAPPY_NETWORK_LEGEND_CONNECTION_LINE_WIDTH_PX;
|
|
355
|
+
context.setLineDash(weightedConnectionScene.dashPattern);
|
|
356
|
+
context.beginPath();
|
|
357
|
+
context.moveTo(
|
|
358
|
+
weightedConnectionScene.fromPosition.xPx,
|
|
359
|
+
weightedConnectionScene.fromPosition.yPx,
|
|
360
|
+
);
|
|
361
|
+
context.lineTo(
|
|
362
|
+
weightedConnectionScene.toPosition.xPx,
|
|
363
|
+
weightedConnectionScene.toPosition.yPx,
|
|
364
|
+
);
|
|
365
|
+
context.stroke();
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Resolves all paint attributes needed to render a single node.
|
|
370
|
+
*
|
|
371
|
+
* @param context - Render context.
|
|
372
|
+
* @param positionedNode - Positioned node payload.
|
|
373
|
+
* @param nodeDimensions - Shared node dimensions.
|
|
374
|
+
* @param halfNodeWidthPx - Cached half node width.
|
|
375
|
+
* @param biasScale - Bias color scale.
|
|
376
|
+
* @returns Paint-ready node scene.
|
|
377
|
+
*/
|
|
378
|
+
function resolveBiasNodeScene(
|
|
379
|
+
context: CanvasRenderingContext2D,
|
|
380
|
+
positionedNode: PositionedNetworkNodeLike,
|
|
381
|
+
nodeDimensions: NetworkNodeDimensionsLike,
|
|
382
|
+
halfNodeWidthPx: number,
|
|
383
|
+
biasScale: DynamicColorScale,
|
|
384
|
+
): BiasNodeScene {
|
|
385
|
+
// Step 1: Resolve color and emphasis rules from node type and bias value.
|
|
386
|
+
const nodeLabel = formatNodeBiasLabel(positionedNode.node.bias);
|
|
387
|
+
const biasNodePaintStyle = resolveBiasNodePaintStyle(
|
|
388
|
+
positionedNode,
|
|
389
|
+
biasScale,
|
|
390
|
+
);
|
|
391
|
+
|
|
392
|
+
// Step 2: Measure the label so the node rectangle can fit readable text.
|
|
393
|
+
const biasNodeLabelMetrics = resolveBiasNodeLabelMetrics(
|
|
394
|
+
context,
|
|
395
|
+
nodeLabel,
|
|
396
|
+
nodeDimensions,
|
|
397
|
+
);
|
|
398
|
+
const resolvedNodeHeightPx = resolveBiasNodeHeightPx(
|
|
399
|
+
nodeDimensions,
|
|
400
|
+
biasNodeLabelMetrics,
|
|
401
|
+
biasNodePaintStyle.isOutputNode,
|
|
402
|
+
);
|
|
403
|
+
const halfNodeHeightPx = resolvedNodeHeightPx * 0.5;
|
|
404
|
+
|
|
405
|
+
// Step 3: Convert center-based node positions into top-left render coordinates.
|
|
406
|
+
return {
|
|
407
|
+
...biasNodePaintStyle,
|
|
408
|
+
positionedNode,
|
|
409
|
+
nodeLabel,
|
|
410
|
+
nodeRectLeftPx: positionedNode.xPx - halfNodeWidthPx,
|
|
411
|
+
nodeRectTopPx: positionedNode.yPx - halfNodeHeightPx,
|
|
412
|
+
resolvedNodeHeightPx,
|
|
413
|
+
labelBaselineYPx:
|
|
414
|
+
positionedNode.yPx +
|
|
415
|
+
(biasNodeLabelMetrics.labelAscentPx -
|
|
416
|
+
biasNodeLabelMetrics.labelDescentPx) *
|
|
417
|
+
0.5,
|
|
418
|
+
labelFont: biasNodeLabelMetrics.labelFont,
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
/**
|
|
423
|
+
* Resolves node fill, stroke, and glow styling.
|
|
424
|
+
*
|
|
425
|
+
* @param positionedNode - Positioned node payload.
|
|
426
|
+
* @param biasScale - Bias color scale.
|
|
427
|
+
* @returns Node paint style.
|
|
428
|
+
*/
|
|
429
|
+
function resolveBiasNodePaintStyle(
|
|
430
|
+
positionedNode: PositionedNetworkNodeLike,
|
|
431
|
+
biasScale: DynamicColorScale,
|
|
432
|
+
): BiasNodePaintStyle {
|
|
433
|
+
// Step 1: Determine whether the node is an output node, which changes both color and emphasis.
|
|
434
|
+
const isOutputNode =
|
|
435
|
+
positionedNode.node.type === FLAPPY_NETWORK_OUTPUT_NODE_TYPE;
|
|
436
|
+
|
|
437
|
+
// Step 2: Resolve the fill and stroke styles for the active node category.
|
|
438
|
+
return {
|
|
439
|
+
isOutputNode,
|
|
440
|
+
nodeFillColor: isOutputNode
|
|
441
|
+
? FLAPPY_NEON_PALETTE.currentRunText
|
|
442
|
+
: resolveTierColor(
|
|
443
|
+
positionedNode.node.bias,
|
|
444
|
+
biasScale.tiers,
|
|
445
|
+
biasScale.aboveTierColor,
|
|
446
|
+
),
|
|
447
|
+
nodeStrokeColor: isOutputNode
|
|
448
|
+
? FLAPPY_NETWORK_OUTPUT_NODE_STROKE_COLOR
|
|
449
|
+
: FLAPPY_NETWORK_HIDDEN_NODE_STROKE_COLOR,
|
|
450
|
+
nodeStrokeWidthPx: isOutputNode
|
|
451
|
+
? FLAPPY_NETWORK_OUTPUT_NODE_STROKE_WIDTH_PX
|
|
452
|
+
: FLAPPY_NETWORK_HIDDEN_NODE_STROKE_WIDTH_PX,
|
|
453
|
+
nodeShadowBlurPx: isOutputNode
|
|
454
|
+
? FLAPPY_NETWORK_OUTPUT_NODE_SHADOW_BLUR_PX
|
|
455
|
+
: 0,
|
|
456
|
+
nodeShadowColor: isOutputNode
|
|
457
|
+
? FLAPPY_NETWORK_OUTPUT_NODE_GLOW_COLOR
|
|
458
|
+
: FLAPPY_TRANSPARENT_CANVAS_COLOR,
|
|
459
|
+
};
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
/**
|
|
463
|
+
* Measures a bias label and resolves its font declaration.
|
|
464
|
+
*
|
|
465
|
+
* @param context - Render context.
|
|
466
|
+
* @param nodeLabel - Bias label string.
|
|
467
|
+
* @param nodeDimensions - Shared node dimensions.
|
|
468
|
+
* @returns Measured label metrics.
|
|
469
|
+
*/
|
|
470
|
+
function resolveBiasNodeLabelMetrics(
|
|
471
|
+
context: CanvasRenderingContext2D,
|
|
472
|
+
nodeLabel: string,
|
|
473
|
+
nodeDimensions: NetworkNodeDimensionsLike,
|
|
474
|
+
): BiasNodeLabelMetrics {
|
|
475
|
+
// Step 1: Clamp label height into the readable range for the current node size.
|
|
476
|
+
const maximumReadableLabelHeightPx = Math.max(
|
|
477
|
+
FLAPPY_NETWORK_MIN_LABEL_HEIGHT_PX,
|
|
478
|
+
Math.floor(nodeDimensions.heightPx),
|
|
479
|
+
);
|
|
480
|
+
const computedLabelHeightPx = Math.floor(
|
|
481
|
+
nodeDimensions.heightPx * FLAPPY_NETWORK_NODE_LABEL_SIZE_RATIO,
|
|
482
|
+
);
|
|
483
|
+
const labelHeightPx = clamp(
|
|
484
|
+
computedLabelHeightPx,
|
|
485
|
+
FLAPPY_NETWORK_MIN_LABEL_HEIGHT_PX,
|
|
486
|
+
maximumReadableLabelHeightPx,
|
|
487
|
+
);
|
|
488
|
+
const labelFont = `${FLAPPY_NETWORK_NODE_LABEL_FONT_WEIGHT} ${labelHeightPx}px ${FLAPPY_MONOSPACE_FONT_FAMILY}`;
|
|
489
|
+
|
|
490
|
+
// Step 2: Measure the label using the resolved font so baseline math stays stable.
|
|
491
|
+
context.font = labelFont;
|
|
492
|
+
context.textBaseline = FLAPPY_CANVAS_TEXT_BASELINE_ALPHABETIC;
|
|
493
|
+
const labelMetrics = context.measureText(nodeLabel);
|
|
494
|
+
const labelAscentPx =
|
|
495
|
+
labelMetrics.actualBoundingBoxAscent || labelHeightPx * 0.72;
|
|
496
|
+
const labelDescentPx =
|
|
497
|
+
labelMetrics.actualBoundingBoxDescent || labelHeightPx * 0.28;
|
|
498
|
+
|
|
499
|
+
return {
|
|
500
|
+
labelAscentPx,
|
|
501
|
+
labelDescentPx,
|
|
502
|
+
measuredLabelHeightPx: Math.max(
|
|
503
|
+
FLAPPY_NETWORK_MIN_LABEL_HEIGHT_PX,
|
|
504
|
+
Math.ceil(labelAscentPx + labelDescentPx),
|
|
505
|
+
),
|
|
506
|
+
labelFont,
|
|
507
|
+
};
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
/**
|
|
511
|
+
* Resolves node rectangle height from label metrics and node role.
|
|
512
|
+
*
|
|
513
|
+
* @param nodeDimensions - Shared node dimensions.
|
|
514
|
+
* @param biasNodeLabelMetrics - Measured label metrics.
|
|
515
|
+
* @param isOutputNode - Whether the node is an output node.
|
|
516
|
+
* @returns Render height for the node rectangle.
|
|
517
|
+
*/
|
|
518
|
+
function resolveBiasNodeHeightPx(
|
|
519
|
+
nodeDimensions: NetworkNodeDimensionsLike,
|
|
520
|
+
biasNodeLabelMetrics: BiasNodeLabelMetrics,
|
|
521
|
+
isOutputNode: boolean,
|
|
522
|
+
): number {
|
|
523
|
+
// Step 1: Use a slightly tighter output-node box while enforcing a hard minimum height.
|
|
524
|
+
if (isOutputNode) {
|
|
525
|
+
return Math.max(
|
|
526
|
+
FLAPPY_NETWORK_MIN_RENDER_NODE_HEIGHT_PX,
|
|
527
|
+
nodeDimensions.heightPx - FLAPPY_NETWORK_OUTPUT_NODE_HEIGHT_REDUCTION_PX,
|
|
528
|
+
);
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
// Step 2: Fit hidden and input nodes around the measured label height plus inner padding.
|
|
532
|
+
return Math.max(
|
|
533
|
+
FLAPPY_NETWORK_MIN_RENDER_NODE_HEIGHT_PX,
|
|
534
|
+
Math.min(
|
|
535
|
+
nodeDimensions.heightPx,
|
|
536
|
+
biasNodeLabelMetrics.measuredLabelHeightPx +
|
|
537
|
+
FLAPPY_NETWORK_HIDDEN_NODE_VERTICAL_PADDING_PX,
|
|
538
|
+
),
|
|
539
|
+
);
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
/**
|
|
543
|
+
* Draws a resolved node rectangle and optional bias label.
|
|
544
|
+
*
|
|
545
|
+
* @param context - Render context.
|
|
546
|
+
* @param biasNodeScene - Paint-ready node scene.
|
|
547
|
+
* @param nodeWidthPx - Shared node width.
|
|
548
|
+
* @returns Nothing.
|
|
549
|
+
*/
|
|
550
|
+
function drawBiasNodeScene(
|
|
551
|
+
context: CanvasRenderingContext2D,
|
|
552
|
+
biasNodeScene: BiasNodeScene,
|
|
553
|
+
nodeWidthPx: number,
|
|
554
|
+
): void {
|
|
555
|
+
// Step 1: Paint the node rectangle with its resolved fill, stroke, and glow emphasis.
|
|
556
|
+
context.fillStyle = biasNodeScene.nodeFillColor;
|
|
557
|
+
context.strokeStyle = biasNodeScene.nodeStrokeColor;
|
|
558
|
+
context.lineWidth = biasNodeScene.nodeStrokeWidthPx;
|
|
559
|
+
context.shadowBlur = biasNodeScene.nodeShadowBlurPx;
|
|
560
|
+
context.shadowColor = biasNodeScene.nodeShadowColor;
|
|
561
|
+
context.fillRect(
|
|
562
|
+
biasNodeScene.nodeRectLeftPx,
|
|
563
|
+
biasNodeScene.nodeRectTopPx,
|
|
564
|
+
nodeWidthPx,
|
|
565
|
+
biasNodeScene.resolvedNodeHeightPx,
|
|
566
|
+
);
|
|
567
|
+
context.strokeRect(
|
|
568
|
+
biasNodeScene.nodeRectLeftPx,
|
|
569
|
+
biasNodeScene.nodeRectTopPx,
|
|
570
|
+
nodeWidthPx,
|
|
571
|
+
biasNodeScene.resolvedNodeHeightPx,
|
|
572
|
+
);
|
|
573
|
+
|
|
574
|
+
// Step 2: Reset shadow state before any later text rendering.
|
|
575
|
+
context.shadowBlur = 0;
|
|
576
|
+
context.shadowColor = FLAPPY_TRANSPARENT_CANVAS_COLOR;
|
|
577
|
+
|
|
578
|
+
// Step 3: Skip label painting for output nodes, which render as emphasized solid blocks.
|
|
579
|
+
if (biasNodeScene.isOutputNode) {
|
|
580
|
+
return;
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
// Step 4: Paint the centered bias label inside the node rectangle.
|
|
584
|
+
context.fillStyle = FLAPPY_NETWORK_NODE_LABEL_FILL_COLOR;
|
|
585
|
+
context.font = biasNodeScene.labelFont;
|
|
586
|
+
context.textAlign = FLAPPY_CANVAS_TEXT_ALIGN_CENTER;
|
|
587
|
+
context.textBaseline = FLAPPY_CANVAS_TEXT_BASELINE_ALPHABETIC;
|
|
588
|
+
context.fillText(
|
|
589
|
+
biasNodeScene.nodeLabel,
|
|
590
|
+
biasNodeScene.positionedNode.xPx,
|
|
591
|
+
biasNodeScene.labelBaselineYPx,
|
|
592
|
+
);
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
/**
|
|
596
|
+
* Determines whether the responsive viewport intentionally hides the overlay legend.
|
|
597
|
+
*
|
|
598
|
+
* @param context - Render context.
|
|
599
|
+
* @returns True when the legend should be omitted.
|
|
600
|
+
*/
|
|
601
|
+
function shouldHideNetworkColorLegend(
|
|
602
|
+
context: CanvasRenderingContext2D,
|
|
603
|
+
): boolean {
|
|
604
|
+
// Step 1: Prefer the live window width when available, otherwise fall back to canvas width.
|
|
605
|
+
const viewportWidthPx =
|
|
606
|
+
context.canvas.ownerDocument?.defaultView?.innerWidth ??
|
|
607
|
+
context.canvas.width;
|
|
608
|
+
return viewportWidthPx < FLAPPY_VIEWPORT_NETWORK_OVERLAY_HIDDEN_BREAKPOINT_PX;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
/**
|
|
612
|
+
* Resolves the legend rows, layout, and architecture label bounds.
|
|
613
|
+
*
|
|
614
|
+
* @param context - Render context.
|
|
615
|
+
* @param architectureLabel - Multiline architecture label.
|
|
616
|
+
* @param colorScales - Connection and bias color scales.
|
|
617
|
+
* @returns Legend scene context.
|
|
618
|
+
*/
|
|
619
|
+
function resolveLegendSceneContext(
|
|
620
|
+
context: CanvasRenderingContext2D,
|
|
621
|
+
architectureLabel: string,
|
|
622
|
+
colorScales: NetworkVisualizationColorScales,
|
|
623
|
+
): LegendSceneContext {
|
|
624
|
+
// Step 1: Build the connection and bias legend rows from the active scales.
|
|
625
|
+
const connectionLegendRows = createColorLegendRows(
|
|
626
|
+
colorScales.connectionScale,
|
|
627
|
+
FLAPPY_CONNECTION_LEGEND_SYMBOL,
|
|
628
|
+
);
|
|
629
|
+
const biasLegendRows = createColorLegendRows(
|
|
630
|
+
colorScales.biasScale,
|
|
631
|
+
FLAPPY_BIAS_LEGEND_SYMBOL,
|
|
632
|
+
);
|
|
633
|
+
const networkLegendLayout = resolveNetworkLegendLayout(
|
|
634
|
+
context,
|
|
635
|
+
connectionLegendRows,
|
|
636
|
+
biasLegendRows,
|
|
637
|
+
);
|
|
638
|
+
|
|
639
|
+
// Step 2: Resolve the multiline architecture label bounds above the legend box.
|
|
640
|
+
const architectureLines = architectureLabel.split(
|
|
641
|
+
FLAPPY_MULTILINE_LABEL_SEPARATOR,
|
|
642
|
+
);
|
|
643
|
+
const architectureTextTopPx = Math.max(
|
|
644
|
+
FLAPPY_NETWORK_LEGEND_MIN_ARCHITECTURE_TOP_PX,
|
|
645
|
+
networkLegendLayout.legendTopPx -
|
|
646
|
+
architectureLines.length * FLAPPY_NETWORK_HEADER_LINE_HEIGHT_PX -
|
|
647
|
+
FLAPPY_NETWORK_LEGEND_ARCHITECTURE_GAP_PX,
|
|
648
|
+
);
|
|
649
|
+
|
|
650
|
+
return {
|
|
651
|
+
...networkLegendLayout,
|
|
652
|
+
architectureLines,
|
|
653
|
+
architectureTextTopPx,
|
|
654
|
+
connectionLegendRows,
|
|
655
|
+
biasLegendRows,
|
|
656
|
+
};
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
/**
|
|
660
|
+
* Draws the architecture label block above the legend frame.
|
|
661
|
+
*
|
|
662
|
+
* @param context - Render context.
|
|
663
|
+
* @param legendSceneContext - Legend scene context.
|
|
664
|
+
* @returns Nothing.
|
|
665
|
+
*/
|
|
666
|
+
function drawLegendArchitectureLabel(
|
|
667
|
+
context: CanvasRenderingContext2D,
|
|
668
|
+
legendSceneContext: LegendSceneContext,
|
|
669
|
+
): void {
|
|
670
|
+
// Step 1: Paint the architecture lines aligned to the legend frame left edge.
|
|
671
|
+
drawLeftAlignedTextRows(context, {
|
|
672
|
+
lines: legendSceneContext.architectureLines,
|
|
673
|
+
leftPx:
|
|
674
|
+
legendSceneContext.legendLeftPx + FLAPPY_NETWORK_LEGEND_BOX_PADDING_PX,
|
|
675
|
+
topPx: legendSceneContext.architectureTextTopPx,
|
|
676
|
+
lineHeightPx: FLAPPY_NETWORK_HEADER_LINE_HEIGHT_PX,
|
|
677
|
+
font: `${FLAPPY_NETWORK_HEADER_FONT_SIZE_PX}px ${FLAPPY_MONOSPACE_FONT_FAMILY}`,
|
|
678
|
+
fillStyle: FLAPPY_NETWORK_HEADER_TEXT_COLOR,
|
|
679
|
+
});
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
/**
|
|
683
|
+
* Draws the legend container frame.
|
|
684
|
+
*
|
|
685
|
+
* @param context - Render context.
|
|
686
|
+
* @param legendSceneContext - Legend scene context.
|
|
687
|
+
* @returns Nothing.
|
|
688
|
+
*/
|
|
689
|
+
function drawLegendFrame(
|
|
690
|
+
context: CanvasRenderingContext2D,
|
|
691
|
+
legendSceneContext: LegendSceneContext,
|
|
692
|
+
): void {
|
|
693
|
+
// Step 1: Paint the legend background block.
|
|
694
|
+
context.fillStyle = FLAPPY_NETWORK_LEGEND_BACKGROUND;
|
|
695
|
+
context.strokeStyle = FLAPPY_NEON_PALETTE.statusText;
|
|
696
|
+
context.lineWidth = 1;
|
|
697
|
+
context.fillRect(
|
|
698
|
+
legendSceneContext.legendLeftPx,
|
|
699
|
+
legendSceneContext.legendTopPx,
|
|
700
|
+
legendSceneContext.legendWidthPx,
|
|
701
|
+
legendSceneContext.legendHeightPx,
|
|
702
|
+
);
|
|
703
|
+
context.strokeRect(
|
|
704
|
+
legendSceneContext.legendLeftPx,
|
|
705
|
+
legendSceneContext.legendTopPx,
|
|
706
|
+
legendSceneContext.legendWidthPx,
|
|
707
|
+
legendSceneContext.legendHeightPx,
|
|
708
|
+
);
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
/**
|
|
712
|
+
* Draws the legend title row.
|
|
713
|
+
*
|
|
714
|
+
* @param context - Render context.
|
|
715
|
+
* @param legendSceneContext - Legend scene context.
|
|
716
|
+
* @returns Nothing.
|
|
717
|
+
*/
|
|
718
|
+
function drawLegendHeader(
|
|
719
|
+
context: CanvasRenderingContext2D,
|
|
720
|
+
legendSceneContext: LegendSceneContext,
|
|
721
|
+
): void {
|
|
722
|
+
// Step 1: Resolve the legend title font based on the compact layout mode.
|
|
723
|
+
const legendFontSizePx = legendSceneContext.compactLegend
|
|
724
|
+
? FLAPPY_NETWORK_LEGEND_COMPACT_FONT_SIZE_PX
|
|
725
|
+
: FLAPPY_NETWORK_LEGEND_REGULAR_FONT_SIZE_PX;
|
|
726
|
+
|
|
727
|
+
// Step 2: Paint the title inside the legend frame.
|
|
728
|
+
context.fillStyle = FLAPPY_NETWORK_LEGEND_HEADER_COLOR;
|
|
729
|
+
context.font = `${legendFontSizePx}px ${FLAPPY_MONOSPACE_FONT_FAMILY}`;
|
|
730
|
+
context.textAlign = FLAPPY_CANVAS_TEXT_ALIGN_LEFT;
|
|
731
|
+
context.textBaseline = FLAPPY_CANVAS_TEXT_BASELINE_TOP;
|
|
732
|
+
context.fillText(
|
|
733
|
+
FLAPPY_NETWORK_LEGEND_TITLE,
|
|
734
|
+
legendSceneContext.legendLeftPx + FLAPPY_NETWORK_LEGEND_BOX_PADDING_PX,
|
|
735
|
+
legendSceneContext.legendTopPx +
|
|
736
|
+
FLAPPY_NETWORK_LEGEND_HEADER_TOP_PADDING_PX,
|
|
737
|
+
);
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
/**
|
|
741
|
+
* Draws the connection-weight legend section.
|
|
742
|
+
*
|
|
743
|
+
* @param context - Render context.
|
|
744
|
+
* @param legendSceneContext - Legend scene context.
|
|
745
|
+
* @returns Nothing.
|
|
746
|
+
*/
|
|
747
|
+
function drawLegendConnectionSection(
|
|
748
|
+
context: CanvasRenderingContext2D,
|
|
749
|
+
legendSceneContext: LegendSceneContext,
|
|
750
|
+
): void {
|
|
751
|
+
// Step 1: Paint the section title just below the legend header.
|
|
752
|
+
const connectionSectionTopPx =
|
|
753
|
+
legendSceneContext.legendTopPx + legendSceneContext.legendHeaderHeightPx;
|
|
754
|
+
context.fillStyle = FLAPPY_NETWORK_LEGEND_CONNECTION_TITLE_COLOR;
|
|
755
|
+
context.font = `${legendSceneContext.compactLegend ? FLAPPY_NETWORK_LEGEND_COMPACT_FONT_SIZE_PX : FLAPPY_NETWORK_LEGEND_REGULAR_FONT_SIZE_PX}px ${FLAPPY_MONOSPACE_FONT_FAMILY}`;
|
|
756
|
+
context.textAlign = FLAPPY_CANVAS_TEXT_ALIGN_LEFT;
|
|
757
|
+
context.textBaseline = FLAPPY_CANVAS_TEXT_BASELINE_TOP;
|
|
758
|
+
context.fillText(
|
|
759
|
+
FLAPPY_NETWORK_CONNECTION_SECTION_TITLE,
|
|
760
|
+
legendSceneContext.legendLeftPx + FLAPPY_NETWORK_LEGEND_BOX_PADDING_PX,
|
|
761
|
+
connectionSectionTopPx,
|
|
762
|
+
);
|
|
763
|
+
|
|
764
|
+
// Step 2: Paint each legend row sample and label.
|
|
765
|
+
legendSceneContext.connectionLegendRows.forEach(
|
|
766
|
+
(connectionLegendRow, connectionLegendRowIndex) => {
|
|
767
|
+
const connectionRowTopPx =
|
|
768
|
+
connectionSectionTopPx +
|
|
769
|
+
legendSceneContext.legendSectionTitleHeightPx +
|
|
770
|
+
connectionLegendRowIndex * legendSceneContext.legendRowHeightPx;
|
|
771
|
+
drawLegendConnectionRow(
|
|
772
|
+
context,
|
|
773
|
+
legendSceneContext,
|
|
774
|
+
connectionLegendRow,
|
|
775
|
+
connectionRowTopPx,
|
|
776
|
+
);
|
|
777
|
+
},
|
|
778
|
+
);
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
/**
|
|
782
|
+
* Draws a single connection legend row.
|
|
783
|
+
*
|
|
784
|
+
* @param context - Render context.
|
|
785
|
+
* @param legendSceneContext - Legend scene context.
|
|
786
|
+
* @param connectionLegendRow - Legend row.
|
|
787
|
+
* @param connectionRowTopPx - Row top coordinate.
|
|
788
|
+
* @returns Nothing.
|
|
789
|
+
*/
|
|
790
|
+
function drawLegendConnectionRow(
|
|
791
|
+
context: CanvasRenderingContext2D,
|
|
792
|
+
legendSceneContext: LegendSceneContext,
|
|
793
|
+
connectionLegendRow: ColorLegendRow,
|
|
794
|
+
connectionRowTopPx: number,
|
|
795
|
+
): void {
|
|
796
|
+
// Step 1: Resolve the sample line coordinates for the row.
|
|
797
|
+
const sampleStartXPx =
|
|
798
|
+
legendSceneContext.legendLeftPx + FLAPPY_NETWORK_LEGEND_BOX_PADDING_PX;
|
|
799
|
+
const sampleEndXPx =
|
|
800
|
+
legendSceneContext.legendLeftPx +
|
|
801
|
+
FLAPPY_NETWORK_LEGEND_CONNECTION_SAMPLE_END_X_PX;
|
|
802
|
+
const sampleYPx =
|
|
803
|
+
connectionRowTopPx + FLAPPY_NETWORK_LEGEND_CONNECTION_SAMPLE_Y_OFFSET_PX;
|
|
804
|
+
|
|
805
|
+
// Step 2: Paint negative ranges with dots and positive ranges with solid strokes.
|
|
806
|
+
if (connectionLegendRow.maximumValue <= 0) {
|
|
807
|
+
drawSquareDottedConnection(context, {
|
|
808
|
+
fromXPx: sampleStartXPx,
|
|
809
|
+
fromYPx: sampleYPx,
|
|
810
|
+
toXPx: sampleEndXPx,
|
|
811
|
+
toYPx: sampleYPx,
|
|
812
|
+
color: connectionLegendRow.color,
|
|
813
|
+
lineWidthPx: FLAPPY_NETWORK_LEGEND_CONNECTION_LINE_WIDTH_PX,
|
|
814
|
+
});
|
|
815
|
+
} else {
|
|
816
|
+
context.strokeStyle = connectionLegendRow.color;
|
|
817
|
+
context.lineWidth = FLAPPY_NETWORK_LEGEND_CONNECTION_LINE_WIDTH_PX;
|
|
818
|
+
context.beginPath();
|
|
819
|
+
context.moveTo(sampleStartXPx, sampleYPx);
|
|
820
|
+
context.lineTo(sampleEndXPx, sampleYPx);
|
|
821
|
+
context.stroke();
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
// Step 3: Paint the row label beside the sample.
|
|
825
|
+
context.fillStyle = FLAPPY_NETWORK_LEGEND_ROW_TEXT_COLOR;
|
|
826
|
+
context.fillText(
|
|
827
|
+
connectionLegendRow.label,
|
|
828
|
+
legendSceneContext.legendLeftPx +
|
|
829
|
+
FLAPPY_NETWORK_LEGEND_CONNECTION_LABEL_X_PX,
|
|
830
|
+
connectionRowTopPx - 1,
|
|
831
|
+
);
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
/**
|
|
835
|
+
* Draws the bias legend section.
|
|
836
|
+
*
|
|
837
|
+
* @param context - Render context.
|
|
838
|
+
* @param legendSceneContext - Legend scene context.
|
|
839
|
+
* @returns Nothing.
|
|
840
|
+
*/
|
|
841
|
+
function drawLegendBiasSection(
|
|
842
|
+
context: CanvasRenderingContext2D,
|
|
843
|
+
legendSceneContext: LegendSceneContext,
|
|
844
|
+
): void {
|
|
845
|
+
// Step 1: Resolve the bias section top from the connection section height.
|
|
846
|
+
const biasSectionTopPx =
|
|
847
|
+
legendSceneContext.legendTopPx +
|
|
848
|
+
legendSceneContext.legendHeaderHeightPx +
|
|
849
|
+
legendSceneContext.legendSectionTitleHeightPx +
|
|
850
|
+
legendSceneContext.connectionLegendRows.length *
|
|
851
|
+
legendSceneContext.legendRowHeightPx +
|
|
852
|
+
legendSceneContext.legendSectionGapPx;
|
|
853
|
+
context.fillStyle = FLAPPY_NETWORK_LEGEND_BIAS_TITLE_COLOR;
|
|
854
|
+
context.font = `${legendSceneContext.compactLegend ? FLAPPY_NETWORK_LEGEND_COMPACT_FONT_SIZE_PX : FLAPPY_NETWORK_LEGEND_REGULAR_FONT_SIZE_PX}px ${FLAPPY_MONOSPACE_FONT_FAMILY}`;
|
|
855
|
+
context.textAlign = FLAPPY_CANVAS_TEXT_ALIGN_LEFT;
|
|
856
|
+
context.textBaseline = FLAPPY_CANVAS_TEXT_BASELINE_TOP;
|
|
857
|
+
context.fillText(
|
|
858
|
+
FLAPPY_NETWORK_BIAS_SECTION_TITLE,
|
|
859
|
+
legendSceneContext.legendLeftPx + FLAPPY_NETWORK_LEGEND_BOX_PADDING_PX,
|
|
860
|
+
biasSectionTopPx,
|
|
861
|
+
);
|
|
862
|
+
|
|
863
|
+
// Step 2: Paint each bias swatch row beneath the section title.
|
|
864
|
+
legendSceneContext.biasLegendRows.forEach(
|
|
865
|
+
(biasLegendRow, biasLegendRowIndex) => {
|
|
866
|
+
const biasRowTopPx =
|
|
867
|
+
biasSectionTopPx +
|
|
868
|
+
legendSceneContext.legendSectionTitleHeightPx +
|
|
869
|
+
biasLegendRowIndex * legendSceneContext.legendRowHeightPx;
|
|
870
|
+
drawLegendBiasRow(
|
|
871
|
+
context,
|
|
872
|
+
legendSceneContext,
|
|
873
|
+
biasLegendRow,
|
|
874
|
+
biasRowTopPx,
|
|
875
|
+
);
|
|
876
|
+
},
|
|
877
|
+
);
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
/**
|
|
881
|
+
* Draws a single bias legend row.
|
|
882
|
+
*
|
|
883
|
+
* @param context - Render context.
|
|
884
|
+
* @param legendSceneContext - Legend scene context.
|
|
885
|
+
* @param biasLegendRow - Legend row.
|
|
886
|
+
* @param biasRowTopPx - Row top coordinate.
|
|
887
|
+
* @returns Nothing.
|
|
888
|
+
*/
|
|
889
|
+
function drawLegendBiasRow(
|
|
890
|
+
context: CanvasRenderingContext2D,
|
|
891
|
+
legendSceneContext: LegendSceneContext,
|
|
892
|
+
biasLegendRow: ColorLegendRow,
|
|
893
|
+
biasRowTopPx: number,
|
|
894
|
+
): void {
|
|
895
|
+
// Step 1: Paint the color swatch that represents the bias bucket.
|
|
896
|
+
context.fillStyle = biasLegendRow.color;
|
|
897
|
+
context.beginPath();
|
|
898
|
+
context.rect(
|
|
899
|
+
legendSceneContext.legendLeftPx + FLAPPY_NETWORK_LEGEND_BIAS_SWATCH_X_PX,
|
|
900
|
+
biasRowTopPx + FLAPPY_NETWORK_LEGEND_BIAS_SWATCH_Y_PX,
|
|
901
|
+
FLAPPY_NETWORK_LEGEND_BIAS_SWATCH_SIZE_PX,
|
|
902
|
+
FLAPPY_NETWORK_LEGEND_BIAS_SWATCH_SIZE_PX,
|
|
903
|
+
);
|
|
904
|
+
context.fill();
|
|
905
|
+
|
|
906
|
+
// Step 2: Paint the descriptive label beside the swatch.
|
|
907
|
+
context.fillStyle = FLAPPY_NETWORK_LEGEND_ROW_TEXT_COLOR;
|
|
908
|
+
context.fillText(
|
|
909
|
+
biasLegendRow.label,
|
|
910
|
+
legendSceneContext.legendLeftPx + FLAPPY_NETWORK_LEGEND_BIAS_LABEL_X_PX,
|
|
911
|
+
biasRowTopPx - 1,
|
|
912
|
+
);
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
/**
|
|
916
|
+
* Draws multiline text rows aligned to a fixed left edge.
|
|
917
|
+
*
|
|
918
|
+
* @param context - Render context.
|
|
919
|
+
* @param request - Multiline text draw request.
|
|
920
|
+
* @returns Nothing.
|
|
921
|
+
*/
|
|
922
|
+
function drawLeftAlignedTextRows(
|
|
923
|
+
context: CanvasRenderingContext2D,
|
|
924
|
+
request: {
|
|
925
|
+
lines: string[];
|
|
926
|
+
leftPx: number;
|
|
927
|
+
topPx: number;
|
|
928
|
+
lineHeightPx: number;
|
|
929
|
+
font: string;
|
|
930
|
+
fillStyle: string;
|
|
931
|
+
},
|
|
932
|
+
): void {
|
|
933
|
+
// Step 1: Configure the shared text state used by every row.
|
|
934
|
+
context.fillStyle = request.fillStyle;
|
|
935
|
+
context.font = request.font;
|
|
936
|
+
context.textAlign = FLAPPY_CANVAS_TEXT_ALIGN_LEFT;
|
|
937
|
+
context.textBaseline = FLAPPY_CANVAS_TEXT_BASELINE_TOP;
|
|
938
|
+
|
|
939
|
+
// Step 2: Paint the rows with fixed vertical spacing.
|
|
940
|
+
request.lines.forEach((lineText, lineIndex) => {
|
|
941
|
+
context.fillText(
|
|
942
|
+
lineText,
|
|
943
|
+
request.leftPx,
|
|
944
|
+
request.topPx + lineIndex * request.lineHeightPx,
|
|
945
|
+
);
|
|
946
|
+
});
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
/**
|
|
950
|
+
* Draws a square-dotted connection stroke for negative weights.
|
|
951
|
+
*
|
|
952
|
+
* @param context - Render context.
|
|
953
|
+
* @param input - Dotted-stroke endpoints and style.
|
|
954
|
+
* @returns Nothing.
|
|
955
|
+
*/
|
|
956
|
+
function drawSquareDottedConnection(
|
|
957
|
+
context: CanvasRenderingContext2D,
|
|
958
|
+
input: {
|
|
959
|
+
fromXPx: number;
|
|
960
|
+
fromYPx: number;
|
|
961
|
+
toXPx: number;
|
|
962
|
+
toYPx: number;
|
|
963
|
+
color: string;
|
|
964
|
+
lineWidthPx: number;
|
|
965
|
+
},
|
|
966
|
+
): void {
|
|
967
|
+
// Step 1: Resolve the normalized segment direction and exit early for zero-length links.
|
|
968
|
+
const deltaXPx = input.toXPx - input.fromXPx;
|
|
969
|
+
const deltaYPx = input.toYPx - input.fromYPx;
|
|
970
|
+
const segmentLengthPx = Math.hypot(deltaXPx, deltaYPx);
|
|
971
|
+
if (segmentLengthPx <= 0) {
|
|
972
|
+
return;
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
// Step 2: Resolve the dot cadence and alignment helpers for the segment.
|
|
976
|
+
const squareSidePx = FLAPPY_NETWORK_DOTTED_CONNECTION_SQUARE_SIDE_PX;
|
|
977
|
+
const stepDistancePx = Math.max(
|
|
978
|
+
(squareSidePx + 2) * FLAPPY_NETWORK_DOTTED_CONNECTION_STEP_COMPACT_RATIO,
|
|
979
|
+
input.lineWidthPx *
|
|
980
|
+
FLAPPY_NETWORK_DOTTED_CONNECTION_WIDTH_SPACING_RATIO *
|
|
981
|
+
FLAPPY_NETWORK_DOTTED_CONNECTION_STEP_COMPACT_RATIO,
|
|
982
|
+
);
|
|
983
|
+
const directionXPx = deltaXPx / segmentLengthPx;
|
|
984
|
+
const directionYPx = deltaYPx / segmentLengthPx;
|
|
985
|
+
const halfSquareSidePx = squareSidePx * 0.5;
|
|
986
|
+
|
|
987
|
+
context.fillStyle = input.color;
|
|
988
|
+
|
|
989
|
+
// Step 3: Paint each aligned square sample along the connection path.
|
|
990
|
+
for (
|
|
991
|
+
let traveledDistancePx = 0;
|
|
992
|
+
traveledDistancePx <= segmentLengthPx;
|
|
993
|
+
traveledDistancePx += stepDistancePx
|
|
994
|
+
) {
|
|
995
|
+
const centerXPx = input.fromXPx + directionXPx * traveledDistancePx;
|
|
996
|
+
const centerYPx = input.fromYPx + directionYPx * traveledDistancePx;
|
|
997
|
+
|
|
998
|
+
const axisAlignedCenterXPx =
|
|
999
|
+
Math.round(
|
|
1000
|
+
centerXPx +
|
|
1001
|
+
directionXPx * FLAPPY_NETWORK_DOTTED_CONNECTION_ALIGNMENT_EPSILON,
|
|
1002
|
+
) -
|
|
1003
|
+
Math.round(
|
|
1004
|
+
directionXPx * FLAPPY_NETWORK_DOTTED_CONNECTION_ALIGNMENT_EPSILON,
|
|
1005
|
+
);
|
|
1006
|
+
const axisAlignedCenterYPx =
|
|
1007
|
+
Math.round(
|
|
1008
|
+
centerYPx +
|
|
1009
|
+
directionYPx * FLAPPY_NETWORK_DOTTED_CONNECTION_ALIGNMENT_EPSILON,
|
|
1010
|
+
) -
|
|
1011
|
+
Math.round(
|
|
1012
|
+
directionYPx * FLAPPY_NETWORK_DOTTED_CONNECTION_ALIGNMENT_EPSILON,
|
|
1013
|
+
);
|
|
1014
|
+
|
|
1015
|
+
context.fillRect(
|
|
1016
|
+
axisAlignedCenterXPx - halfSquareSidePx,
|
|
1017
|
+
axisAlignedCenterYPx - halfSquareSidePx,
|
|
1018
|
+
squareSidePx,
|
|
1019
|
+
squareSidePx,
|
|
1020
|
+
);
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
// Step 4: Paint the destination endpoint to avoid a visible trailing gap.
|
|
1024
|
+
context.fillRect(
|
|
1025
|
+
Math.round(input.toXPx - halfSquareSidePx),
|
|
1026
|
+
Math.round(input.toYPx - halfSquareSidePx),
|
|
1027
|
+
squareSidePx,
|
|
1028
|
+
squareSidePx,
|
|
1029
|
+
);
|
|
70
1030
|
}
|