From 932f8cc5eb0c0ae30d709a667664486b38c95b30 Mon Sep 17 00:00:00 2001 From: Luc Mcgrady Date: Sat, 8 Nov 2025 17:15:18 +0000 Subject: [PATCH] Fix: Add minimum bin width --- ts/routes/graphs/difficulty.ts | 30 +++-------------------------- ts/routes/graphs/percentageRange.ts | 30 ++++++++++++++++++++++++++++- ts/routes/graphs/retrievability.ts | 30 +++-------------------------- 3 files changed, 35 insertions(+), 55 deletions(-) diff --git a/ts/routes/graphs/difficulty.ts b/ts/routes/graphs/difficulty.ts index 9ab1d82bc..4f36ecdcf 100644 --- a/ts/routes/graphs/difficulty.ts +++ b/ts/routes/graphs/difficulty.ts @@ -8,13 +8,13 @@ import type { GraphsResponse } from "@generated/anki/stats_pb"; import * as tr from "@generated/ftl"; import { localizedNumber } from "@tslib/i18n"; -import type { Bin, ScaleLinear } from "d3"; -import { bin, interpolateRdYlGn, scaleLinear, scaleSequential, sum } from "d3"; +import type { Bin } from "d3"; +import { bin, interpolateRdYlGn, scaleSequential, sum } from "d3"; import type { SearchDispatch, TableDatum } from "./graph-helpers"; import { getNumericMapBinValue, numericMap } from "./graph-helpers"; import type { HistogramData } from "./histogram-graph"; -import { percentageRangeMinMax } from "./percentageRange"; +import { getAdjustedScaleAndTicks, percentageRangeMinMax } from "./percentageRange"; export interface GraphData { eases: Map; @@ -34,30 +34,6 @@ function makeQuery(start: number, end: number): string { return `${fromQuery} AND ${tillQuery}`; } -function getAdjustedScaleAndTicks( - min: number, - max: number, - desiredBars: number, -): [ScaleLinear, number[]] { - const prescale = scaleLinear().domain([min, max]).nice(); - const ticks = prescale.ticks(desiredBars); - - const predomain = prescale.domain() as [number, number]; - - const minOffset = min - predomain[0]; - const tickSize = ticks[1] - ticks[0]; - - if (minOffset === 0 || (minOffset % tickSize !== 0 && tickSize % minOffset !== 0)) { - return [prescale, ticks]; - } - - const add = (n: number): number => n + minOffset; - return [ - scaleLinear().domain(predomain.map(add) as [number, number]), - ticks.map(add), - ]; -} - export function prepareData( data: GraphData, dispatch: SearchDispatch, diff --git a/ts/routes/graphs/percentageRange.ts b/ts/routes/graphs/percentageRange.ts index 7fcfb3887..163e51ab8 100644 --- a/ts/routes/graphs/percentageRange.ts +++ b/ts/routes/graphs/percentageRange.ts @@ -1,6 +1,6 @@ // Copyright: Ankitects Pty Ltd and contributors -import { sum } from "d3"; +import { range, type ScaleLinear, scaleLinear, sum } from "d3"; // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html export enum PercentageRangeEnum { @@ -33,3 +33,31 @@ export function percentageRangeMinMax(data: Map, range: number | return [xMin, xMax]; } + +export function getAdjustedScaleAndTicks( + min: number, + max: number, + desiredBars: number, +): [ScaleLinear, number[]] { + const prescale = scaleLinear().domain([min, max]).nice(); + let ticks = prescale.ticks(desiredBars); + + const predomain = prescale.domain() as [number, number]; + + const minOffset = min - predomain[0]; + const tickSize = ticks[1] - ticks[0]; + + if (tickSize < 1) { + ticks = range(min, max); + } + + if (minOffset === 0 || (minOffset % tickSize !== 0 && tickSize % minOffset !== 0)) { + return [prescale, ticks]; + } + + const add = (n: number): number => n + minOffset; + return [ + scaleLinear().domain(predomain.map(add) as [number, number]), + ticks.map(add), + ]; +} diff --git a/ts/routes/graphs/retrievability.ts b/ts/routes/graphs/retrievability.ts index 48d7ad6c9..6a1a391d2 100644 --- a/ts/routes/graphs/retrievability.ts +++ b/ts/routes/graphs/retrievability.ts @@ -8,13 +8,13 @@ import type { GraphsResponse } from "@generated/anki/stats_pb"; import * as tr from "@generated/ftl"; import { localizedNumber } from "@tslib/i18n"; -import type { Bin, ScaleLinear } from "d3"; -import { bin, interpolateRdYlGn, scaleLinear, scaleSequential, sum } from "d3"; +import type { Bin } from "d3"; +import { bin, interpolateRdYlGn, scaleSequential, sum } from "d3"; import type { SearchDispatch, TableDatum } from "./graph-helpers"; import { getNumericMapBinValue, numericMap } from "./graph-helpers"; import type { HistogramData } from "./histogram-graph"; -import { percentageRangeMinMax } from "./percentageRange"; +import { getAdjustedScaleAndTicks, percentageRangeMinMax } from "./percentageRange"; export interface GraphData { retrievability: Map; @@ -41,30 +41,6 @@ function makeQuery(start: number, end: number): string { return `${fromQuery} AND ${tillQuery}`; } -function getAdjustedScaleAndTicks( - min: number, - max: number, - desiredBars: number, -): [ScaleLinear, number[]] { - const prescale = scaleLinear().domain([min, max]).nice(); - const ticks = prescale.ticks(desiredBars); - - const predomain = prescale.domain() as [number, number]; - - const minOffset = min - predomain[0]; - const tickSize = ticks[1] - ticks[0]; - - if (minOffset === 0 || (minOffset % tickSize !== 0 && tickSize % minOffset !== 0)) { - return [prescale, ticks]; - } - - const add = (n: number): number => n + minOffset; - return [ - scaleLinear().domain(predomain.map(add) as [number, number]), - ticks.map(add), - ]; -} - export function prepareData( data: GraphData, dispatch: SearchDispatch,