Skip to content

Commit

Permalink
fix: goal chart placement
Browse files Browse the repository at this point in the history
  • Loading branch information
nickofthyme committed Mar 4, 2022
1 parent f150848 commit 2fa9897
Show file tree
Hide file tree
Showing 19 changed files with 97 additions and 40 deletions.
6 changes: 6 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -468,5 +468,11 @@ module.exports = {
'promise/no-promise-in-callback': 0,
},
},
{
files: ['./integration/**/*.test.ts?(x)'],
rules: {
'jest/expect-expect': 0,
},
},
],
};
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.
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.
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.
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.
29 changes: 29 additions & 0 deletions integration/tests/goal_stories.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,4 +70,33 @@ describe('Goal stories', () => {
);
});
});

describe('sagitta shifted goal charts', () => {
it.each<[title: string, startAngle: number, endAngle: number]>([
// top openings
['π/8 -> neg π', 1 / 8, -1],
['neg π/8 -> neg π', -1 / 8, -1],
['neg π -> π/8', -1, 1 / 8],
['neg π -> neg π/8', -1, -1 / 8],
['neg π/4 -> neg 3π/4', -1 / 4, -3 / 4],
['neg 3π/4 -> neg π/4', -3 / 4, -1 / 4],
])('should apply correct top shift (%s)', async (_, startAngle, endAngle) => {
await common.expectChartAtUrlToMatchScreenshot(
`http://localhost:9001/?path=/story/goal-alpha--full-circle&globals=theme:light&knob-startAngle%20(%CF%80)=${startAngle}&knob-endAngle%20(%CF%80)=${endAngle}`,
);
});
it.each<[title: string, startAngle: number, endAngle: number]>([
// bottom openings
['π -> 0', 1, 0],
['3π/4 -> 0', 3 / 4, 0],
['neg π/8 -> π', -1 / 8, 1],
['π/8 -> π', 1 / 8, 1],
['3π/4 -> π/4', 3 / 4, 1 / 4],
['π/4 -> 3π/4', 1 / 4, 3 / 4],
])('should apply correct bottom shift (%s)', async (_, startAngle, endAngle) => {
await common.expectChartAtUrlToMatchScreenshot(
`http://localhost:9001/?path=/story/goal-alpha--full-circle&globals=theme:light&knob-startAngle%20(%CF%80)=${startAngle}&knob-endAngle%20(%CF%80)=${endAngle}`,
);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { Dimensions } from '../../../../utils/dimensions';
import { Theme } from '../../../../utils/themes/theme';
import { GoalSubtype } from '../../specs/constants';
import { BulletViewModel } from '../types/viewmodel_types';
import { getSagitta, getMinSagitta } from './utils';
import { getSagitta, getMinSagitta, getTranformDirection } from './utils';

const referenceCircularSizeCap = 360; // goal/gauge won't be bigger even if there's ample room: it'd be a waste of space
const referenceBulletSizeCap = 500; // goal/gauge won't be bigger even if there's ample room: it'd be a waste of space
Expand Down Expand Up @@ -393,9 +393,11 @@ export function geoms(
const r = 0.5 * referenceSize - maxWidth / 2;

if (circular) {
// This logic is only applicable to angles where -2π > θ > 2π with the smallest angle from 0
const sagitta = getMinSagitta(angleStart, angleEnd, r);
const maxSagitta = getSagitta((3 / 2) * Math.PI, r);
data.yOffset.value = sagitta >= maxSagitta ? 0 : (maxSagitta - sagitta) / 2;
const direction = getTranformDirection(angleStart, angleEnd);
data.yOffset.value = Math.abs(sagitta) >= maxSagitta ? 0 : (direction * (maxSagitta - sagitta)) / 2;
}

const fullSize = referenceSize;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,39 @@ import { round } from '../../../../utils/common';
*/
const LIMITING_ANGLE = Math.PI / 2;

const hasTopGap = (startAngle: Radian, endAngle: Radian): boolean => {
const [a, b] = [startAngle, endAngle].sort();
return a <= -Math.PI / 2 && a >= (-Math.PI * 3) / 2 && b >= -Math.PI / 2 && b <= Math.PI / 2;
};

const hasBottomGap = (startAngle: Radian, endAngle: Radian): boolean => {
const [a, b] = [startAngle, endAngle].sort();
return a >= -Math.PI / 2 && a <= Math.PI / 2 && b < (Math.PI * 3) / 2 && b >= Math.PI / 2;
};

const isOnlyTopHalf = (startAngle: Radian, endAngle: Radian): boolean => {
const [a, b] = [startAngle, endAngle].sort();
return a >= 0 && b <= Math.PI;
};

const isOnlyBottomHalf = (startAngle: Radian, endAngle: Radian): boolean => {
const [a, b] = [startAngle, endAngle].sort();
return (a >= Math.PI && b <= 2 * Math.PI) || (a >= -Math.PI && b <= 0);
};

/** @internal */
export const getTranformDirection = (startAngle: Radian, endAngle: Radian): 1 | -1 =>
hasTopGap(startAngle, endAngle) || isOnlyBottomHalf(startAngle, endAngle) ? -1 : 1;

/**
* Returns limiting angle form π/2 towards 3/2π from left and right
* Returns limiting angle form π/2 towards 3/2π from left and right, top and bottom
*/
const controllingAngle = (...angles: Radian[]): Radian =>
angles.reduce((limitAngle, a) => {
if (a >= Math.PI / 2 && a <= (3 / 2) * Math.PI) {
const newA = Math.abs(a - Math.PI / 2);
return Math.max(limitAngle, newA);
}
if (a >= -Math.PI / 2 && a <= Math.PI / 2) {
const newA = Math.abs(a - Math.PI / 2);
return Math.max(limitAngle, newA);
}
return limitAngle;
}, LIMITING_ANGLE);
const controllingAngle = (startAngle: Radian, endAngle: Radian): number => {
if (isOnlyTopHalf(startAngle, endAngle) || isOnlyBottomHalf(startAngle, endAngle)) return LIMITING_ANGLE;
if (!hasTopGap(startAngle, endAngle) && !hasBottomGap(startAngle, endAngle)) return LIMITING_ANGLE * 2;
const offset = hasBottomGap(startAngle, endAngle) ? -Math.PI / 2 : Math.PI / 2;
return Math.max(Math.abs(startAngle + offset), Math.abs(endAngle + offset), LIMITING_ANGLE);
};

/** @internal */
export function getSagitta(angle: Radian, radius: number, fractionDigits: number = 1) {
Expand Down
52 changes: 27 additions & 25 deletions storybook/stories/goal/17_total_circle.story.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,15 @@
* Side Public License, v 1.
*/

import { number } from '@storybook/addon-knobs';
import React from 'react';

import { Chart, Goal, Settings } from '@elastic/charts';
import { BandFillColorAccessorInput } from '@elastic/charts/src/chart_types/goal_chart/specs';
import { GoalSubtype } from '@elastic/charts/src/chart_types/goal_chart/specs/constants';

import { Color } from '../../../packages/charts/src/common/colors';
import { useBaseTheme } from '../../use_base_theme';

const subtype = GoalSubtype.Goal;

const colorMap: { [k: number]: Color } = {
200: '#fc8d62',
250: 'lightgrey',
Expand All @@ -25,25 +23,29 @@ const colorMap: { [k: number]: Color } = {

const bandFillColor = (x: number): Color => colorMap[x];

export const Example = () => (
<Chart>
<Settings baseTheme={useBaseTheme()} />
<Goal
id="spec_1"
subtype={subtype}
base={0}
target={260}
actual={280}
bands={[200, 250, 300]}
ticks={[0, 50, 100, 150, 200, 250, 265, 280]}
tickValueFormatter={({ value }: BandFillColorAccessorInput) => String(value)}
bandFillColor={({ value }: BandFillColorAccessorInput) => bandFillColor(value)}
labelMajor=""
labelMinor=""
centralMajor="280 MB/s"
centralMinor=""
angleStart={Math.PI + Math.PI / 2}
angleEnd={-Math.PI / 2}
/>
</Chart>
);
export const Example = () => {
const start = number('startAngle (π)', 1.5, { min: -2, max: 2, step: 1 / 8 });
const end = number('endAngle (π)', -0.5, { min: -2, max: 2, step: 1 / 8 });
return (
<Chart>
<Settings baseTheme={useBaseTheme()} />
<Goal
id="spec_1"
subtype={GoalSubtype.Goal}
base={0}
target={260}
actual={280}
bands={[200, 250, 300]}
ticks={[0, 50, 100, 150, 200, 250, 265, 280]}
tickValueFormatter={({ value }) => String(value)}
bandFillColor={({ value }) => bandFillColor(value)}
labelMajor=""
labelMinor=""
centralMajor="280 MB/s"
centralMinor=""
angleStart={start * Math.PI}
angleEnd={end * Math.PI}
/>
</Chart>
);
};

0 comments on commit 2fa9897

Please sign in to comment.