Skip to content

Commit

Permalink
feat: no finer raster than the minInterval
Browse files Browse the repository at this point in the history
  • Loading branch information
monfera committed Oct 25, 2021
1 parent 2b388ba commit 5a4ee6c
Show file tree
Hide file tree
Showing 8 changed files with 66 additions and 29 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 10 additions & 0 deletions integration/tests/axis_stories.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,16 @@ describe('Axis stories', () => {
'http://localhost:9001/?path=/story/bar-chart--test-discover&globals=theme:light&knob-Minor%20grid%20lines=true&knob-Pan%20time=0&knob-Shift%20time=0&knob-Shorter%20X%20axis%20minor%20whiskers=true&knob-Stretch%20time=3.8&knob-Time%20zoom=120&knob-X%20axis%20minor%20whiskers=true&knob-layerCount=3&knob-showOverlappingLabels%20time%20axis=true&knob-showOverlappingTicks%20time%20axis=true&knob-use%20custom%20minInterval%20of%2030s=false&knob-use%20multilayer%20time%20axis=true',
);
});
it('should not show a raster that is finer than the bin width (minInterval)', async () => {
await common.expectChartAtUrlToMatchScreenshot(
'http://localhost:9001/?path=/story/area-chart--timeslip&Bin%20width%20in%20ms%20(0:%20none%20specifed)=60000&Minor%20grid%20lines=on&Shift%20time=0&Stretch%20time=-0.4&Time%20zoom=2&globals=theme:light&knob-Bin%20width%20in%20ms%20(0:%20none%20specifed)=60000&knob-Minor%20grid%20lines=true&knob-Shift%20time=0&knob-Stretch%20time=-0.4&knob-Time%20zoom=2&knob-layerCount=3&layerCount=3',
);
});
it('can show a finer raster than the data bin width if minInterval is expressly specified to be low', async () => {
await common.expectChartAtUrlToMatchScreenshot(
'http://localhost:9001/?path=/story/area-chart--timeslip&Bin%20width%20in%20ms%20(0:%20none%20specifed)=1&Minor%20grid%20lines=on&Shift%20time=0&Stretch%20time=-0.4&Time%20zoom=2&globals=theme:light&knob-Bin%20width%20in%20ms%20(0:%20none%20specifed)=1&knob-Minor%20grid%20lines=true&knob-Shift%20time=0&knob-Stretch%20time=-0.4&knob-Time%20zoom=2&knob-layerCount=3&layerCount=3',
);
});
it('should render proper tick count', async () => {
await common.expectChartAtUrlToMatchScreenshot(
'http://localhost:9001/?path=/story/axes--basic&knob-Tick Label Padding=0&knob-debug=&knob-Bottom overlap labels=&knob-Bottom overlap ticks=true&knob-Number of ticks on bottom=20&knob-Left overlap labels=&knob-Left overlap ticks=true&knob-Number of ticks on left=10',
Expand Down
12 changes: 12 additions & 0 deletions packages/charts/src/chart_types/xy_chart/axes/timeslip/rasters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export interface TimeRaster<T extends TimeBin> {
detailedLabelFormat: (time: number) => string;
minorTickLabelFormat: (time: number) => string;
minimumPixelsPerSecond: number;
approxWidthInMs: number;
}

interface RasterConfig {
Expand Down Expand Up @@ -121,6 +122,7 @@ export const rasters = (
detailedLabelFormat: new Intl.DateTimeFormat(locale, { year: 'numeric', timeZone }).format,
minorTickLabelFormat: new Intl.DateTimeFormat(locale, { year: 'numeric', timeZone }).format,
minimumPixelsPerSecond: NaN,
approxWidthInMs: NaN,
};
const yearsUnlabelled: TimeRaster<TimeBin & { year: number }> = {
...years,
Expand Down Expand Up @@ -150,6 +152,7 @@ export const rasters = (
detailedLabelFormat: new Intl.DateTimeFormat(locale, { year: 'numeric', timeZone }).format,
minorTickLabelFormat: new Intl.DateTimeFormat(locale, { year: 'numeric', timeZone }).format,
minimumPixelsPerSecond: NaN,
approxWidthInMs: NaN,
};
const decadesUnlabelled: TimeRaster<TimeBin & { year: number }> = {
...decades,
Expand Down Expand Up @@ -179,6 +182,7 @@ export const rasters = (
detailedLabelFormat: new Intl.DateTimeFormat(locale, { year: 'numeric', month: 'long', timeZone }).format,
minorTickLabelFormat: new Intl.DateTimeFormat(locale, { month: 'long', timeZone }).format,
minimumPixelsPerSecond: NaN,
approxWidthInMs: NaN,
};
const shortMonths = {
...months,
Expand Down Expand Up @@ -228,6 +232,7 @@ export const rasters = (
return `${numberString}${number === 1 ? 'st' : number === 2 ? 'nd' : number === 3 ? 'rd' : 'th'}`;
},
minimumPixelsPerSecond: NaN,
approxWidthInMs: NaN,
};
const weekStartDays: TimeRaster<TimeBin & { dayOfMonth: number }> = {
unit: 'week',
Expand All @@ -251,6 +256,7 @@ export const rasters = (
minorTickLabelFormat: (d) => `${minorDayFormat(d)}th`,
detailedLabelFormat: detailedDayFormat,
minimumPixelsPerSecond: NaN,
approxWidthInMs: NaN,
};
const daysUnlabelled: TimeRaster<TimeBin & YearToDay> = {
...days,
Expand Down Expand Up @@ -285,6 +291,7 @@ export const rasters = (
timeZone,
}).format,
minimumPixelsPerSecond: NaN,
approxWidthInMs: NaN,
};
const hoursUnlabelled = {
...hours,
Expand Down Expand Up @@ -334,6 +341,7 @@ export const rasters = (
timeZone,
}).format,
minimumPixelsPerSecond: NaN,
approxWidthInMs: NaN,
};
const sixHoursUnlabelled = {
...sixHours,
Expand All @@ -360,6 +368,7 @@ export const rasters = (
timeZone,
}).format(d)}'`,
minimumPixelsPerSecond: NaN,
approxWidthInMs: NaN,
};
const quarterHours = {
...minutes,
Expand Down Expand Up @@ -411,6 +420,7 @@ export const rasters = (
timeZone,
}).format(d)}"`,
minimumPixelsPerSecond: NaN,
approxWidthInMs: NaN,
};
const quarterMinutes = {
...seconds,
Expand Down Expand Up @@ -450,6 +460,7 @@ export const rasters = (
minorTickLabelFormat: (d) => `${d % 1000}ms`,
detailedLabelFormat: (d) => `${d % 1000}ms`,
minimumPixelsPerSecond: NaN,
approxWidthInMs: NaN,
};
const tenMilliseconds = {
...milliseconds,
Expand Down Expand Up @@ -518,6 +529,7 @@ export const rasters = (
// enrich with derived data; Object.assign preserves object identity
.map((r) =>
Object.assign(r, {
approxWidthInMs: approxWidthsInSeconds[r.unit] * r.unitMultiplier * 1000,
minimumPixelsPerSecond: r.minimumTickPixelDistance / (approxWidthsInSeconds[r.unit] * r.unitMultiplier),
}),
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export type Projection = { ticks: AxisTick[]; labelBox: TickLabelBounds; scale:
type Projections = Map<AxisId, Projection>;

const adaptiveTickCount = true;
const WIDTH_FUDGE = 1.05; // raster bin widths are sometimes approximate, but there's no raster that's just 5% denser/sparser, so it's safe

function axisMinMax(axisPosition: Position, chartRotation: Rotation, { width, height }: Size): [number, number] {
const horizontal = isHorizontalAxis(axisPosition);
Expand Down Expand Up @@ -205,10 +206,12 @@ export const getVisibleTickSetsSelector = createCustomCachedSelector(
getVisibleTickSets,
);

const notTooDense = (domainFrom: number, domainTo: number, cartesianWidth: number) => (raster: TimeRaster<TimeBin>) => {
const notTooDense = (domainFrom: number, domainTo: number, binWidth: number, cartesianWidth: number) => (
raster: TimeRaster<TimeBin>,
) => {
const domainInSeconds = domainTo - domainFrom;
const pixelsPerSecond = cartesianWidth / domainInSeconds;
return pixelsPerSecond > raster.minimumPixelsPerSecond;
return pixelsPerSecond > raster.minimumPixelsPerSecond && raster.approxWidthInMs * WIDTH_FUDGE >= binWidth;
};

const getRasterSelector = (timeZone: string, maxLabelRowCount: number): ReturnType<typeof rasters> => {
Expand Down Expand Up @@ -406,9 +409,10 @@ function getVisibleTickSets(
const domainValues = domain.domain; // todo consider a property or object type rename
const domainFromS = Number((domain && domainValues[0]) || NaN) / 1000; // todo rely on a type guard or check rather than conversion
const extendByOneBin = isX && xDomain.isBandScale && enableHistogramMode;
const domainExtension = extendByOneBin ? xDomain.minInterval : 0;
const binWidth = xDomain.minInterval;
const domainExtension = extendByOneBin ? binWidth : 0;
const domainToS = (((domain && Number(domainValues[domainValues.length - 1])) || NaN) + domainExtension) / 1000;
const layers = rasterSelector(notTooDense(domainFromS, domainToS, Math.abs(range[1] - range[0])));
const layers = rasterSelector(notTooDense(domainFromS, domainToS, binWidth, Math.abs(range[1] - range[0])));
let layerIndex = 0;
return acc.set(
axisId,
Expand Down
61 changes: 36 additions & 25 deletions storybook/stories/area/21_with_time_timeslip.story.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,34 +56,45 @@ export const Example = () => {
const minorGridLines = boolean('Minor grid lines', true);
const horizontalAxisTitle = boolean('Horizontal axis title', false);
const yAxisTitle = 'CPU utilization';
const timeZoom =
0 ||
number('Time zoom', data.length, {
range: true,
min: 0,
max: data.length,
step: 1,
});
const timeStretch =
0 ||
number('Stretch time', -0.4, {
range: true,
min: -20,
max: 18,
step: 0.2,
});
const timeShift =
0 ||
number('Shift time', 0, {
range: true,
min: -10,
max: 10,
step: 0.05,
});
const timeZoom = number('Time zoom', data.length, {
range: true,
min: 0,
max: data.length,
step: 1,
});
const timeStretch = number('Stretch time', -0.4, {
range: true,
min: -20,
max: 18,
step: 0.2,
});
const timeShift = number('Shift time', 0, {
range: true,
min: -10,
max: 10,
step: 0.05,
});
const binWidth = number('Bin width in ms (0: none specifed)', 0, {
range: false,
min: 0,
max: 10 * 365 * 24 * 60 * 60 * 1000,
step: 1,
});

return (
<Chart>
<Settings baseTheme={useBaseTheme()} />
<Settings
baseTheme={useBaseTheme()}
xDomain={
binWidth > 0
? {
min: NaN,
max: NaN,
minInterval: binWidth,
}
: undefined
}
/>
<Axis
id="x_minor"
position={Position.Bottom}
Expand Down

0 comments on commit 5a4ee6c

Please sign in to comment.