Fix: Add minimum bin width

This commit is contained in:
Luc Mcgrady 2025-11-08 17:15:18 +00:00
parent 372d15578f
commit 932f8cc5eb
No known key found for this signature in database
GPG key ID: 4F3D7A0B17CC3D9C
3 changed files with 35 additions and 55 deletions

View file

@ -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<number, number>;
@ -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, number, never>, 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,

View file

@ -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<number, number>, range: number |
return [xMin, xMax];
}
export function getAdjustedScaleAndTicks(
min: number,
max: number,
desiredBars: number,
): [ScaleLinear<number, number, never>, 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),
];
}

View file

@ -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<number, number>;
@ -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, number, never>, 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,