From 17d8528afd2ffb2aea6bf3bbe654354732170aa0 Mon Sep 17 00:00:00 2001 From: Luc Mcgrady Date: Sun, 13 Jul 2025 13:38:26 +0100 Subject: [PATCH] Add zoomed version of graph --- ts/routes/deck-options/SimulatorModal.svelte | 117 ++++++++++++------- ts/routes/graphs/simulator.ts | 43 +++++++ 2 files changed, 119 insertions(+), 41 deletions(-) diff --git a/ts/routes/deck-options/SimulatorModal.svelte b/ts/routes/deck-options/SimulatorModal.svelte index 2defe1a0f..ce998ad64 100644 --- a/ts/routes/deck-options/SimulatorModal.svelte +++ b/ts/routes/deck-options/SimulatorModal.svelte @@ -13,9 +13,13 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html import TableData from "../graphs/TableData.svelte"; import InputBox from "../graphs/InputBox.svelte"; import { defaultGraphBounds, type TableDatum } from "../graphs/graph-helpers"; - import { SimulateSubgraph, type Point } from "../graphs/simulator"; + import { + SimulateSubgraph, + SimulateWorkloadSubgraph, + type Point, + } from "../graphs/simulator"; import * as tr from "@generated/ftl"; - import { renderSimulationChart } from "../graphs/simulator"; + import { renderSimulationChart, renderWorkloadChart } from "../graphs/simulator"; import { computeOptimalRetention, simulateFsrsReview, @@ -48,6 +52,9 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html const config = state.currentConfig; let simulateSubgraph: SimulateSubgraph = SimulateSubgraph.count; + let simulateWorkloadSubgraph: SimulateWorkloadSubgraph = + SimulateWorkloadSubgraph.ratio; + let workload: boolean = false; let tableData: TableDatum[] = []; let simulating: boolean = false; const fsrs = state.fsrs; @@ -196,23 +203,20 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html } finally { simulating = false; if (resp) { - simulationNumber += 1; + points = Object.entries(resp.memorized).map(([dr, v]) => ({ + x: parseInt(dr), + timeCost: resp!.cost[dr], + memorized: v, + count: -1, + label: 1, + })); - points = points.concat( - Object.entries(resp.memorized).map(([dr, v]) => ({ - x: parseInt(dr), - timeCost: resp!.cost[dr], - count: resp!.cost[dr] / v, - memorized: v, - label: simulationNumber, - })), - ); - - tableData = renderSimulationChart( + workload = true; + tableData = renderWorkloadChart( svg as SVGElement, bounds, points, - simulateSubgraph, + simulateWorkloadSubgraph, ); } } @@ -266,11 +270,15 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html }); } - tableData = renderSimulationChart( + const render_function = workload + ? renderWorkloadChart + : renderSimulationChart; + + tableData = render_function( svg as SVGElement, bounds, pointsToRender, - simulateSubgraph, + (workload ? simulateWorkloadSubgraph : simulateSubgraph) as any as never, ); } @@ -519,30 +527,57 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
- - - + {#if !workload} + + + + {:else} + + + + {/if}
diff --git a/ts/routes/graphs/simulator.ts b/ts/routes/graphs/simulator.ts index 60e189c5a..a2f4027a0 100644 --- a/ts/routes/graphs/simulator.ts +++ b/ts/routes/graphs/simulator.ts @@ -43,6 +43,49 @@ export enum SimulateWorkloadSubgraph { memorized, } +export function renderWorkloadChart( + svgElem: SVGElement, + bounds: GraphBounds, + data: Point[], + subgraph: SimulateWorkloadSubgraph, +) { + const today = new Date(); + + const xMin = 70; + const xMax = 99; + + const x = scaleLinear() + .domain([xMin, xMax]) + .range([bounds.marginLeft, bounds.width - bounds.marginRight]); + + const subgraph_data = ({ + [SimulateWorkloadSubgraph.ratio]: data.map(d => ({ ...d, y: d.timeCost / d.memorized })), + [SimulateWorkloadSubgraph.time]: data.map(d => ({ ...d, y: d.timeCost })), + [SimulateWorkloadSubgraph.memorized]: data.map(d => ({ ...d, y: d.memorized })), + })[subgraph]; + + const yTickFormat = (n: number): string => { + return subgraph == SimulateWorkloadSubgraph.time ? timeSpan(n, true) : n.toString(); + }; + + const formatY: (value: number) => string = ({ + [SimulateWorkloadSubgraph.ratio]: (value: number) => `${timeSpan(value)} time per 1 card memorized`, + [SimulateWorkloadSubgraph.time]: timeSpan, + [SimulateWorkloadSubgraph.memorized]: (value: number) => + tr.statisticsMemorized({ memorized: Math.round(value).toFixed(0) }), + })[subgraph]; + + return _renderSimulationChart( + svgElem, + bounds, + subgraph_data, + x, + yTickFormat, + formatY, + (_e: MouseEvent, _d: number) => undefined, + ); +} + export function renderSimulationChart( svgElem: SVGElement, bounds: GraphBounds,