Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: adjust domain & range for single value histogram #265

Merged
merged 4 commits into from
Jul 17, 2019
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions src/lib/axes/axis_utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,23 @@ export function computeAxisTicksDimensions(
chartRotation: Rotation,
axisConfig: AxisConfig,
barsPadding?: number,
enableHistogramMode?: boolean,
): AxisTicksDimensions | null {
if (axisSpec.hide) {
return null;
}

const scale = getScaleForAxisSpec(axisSpec, xDomain, yDomain, totalBarsInCluster, chartRotation, 0, 1, barsPadding);
const scale = getScaleForAxisSpec(
axisSpec,
xDomain,
yDomain,
totalBarsInCluster,
chartRotation,
0,
1,
barsPadding,
enableHistogramMode,
);
if (!scale) {
throw new Error(`Cannot compute scale for axis spec ${axisSpec.id}`);
}
Expand Down Expand Up @@ -112,6 +123,7 @@ export function getScaleForAxisSpec(
minRange: number,
maxRange: number,
barsPadding?: number,
enableHistogramMode?: boolean,
): Scale | null {
const axisIsYDomain = isYDomain(axisSpec.position, chartRotation);

Expand All @@ -122,7 +134,7 @@ export function getScaleForAxisSpec(
}
return null;
} else {
return computeXScale(xDomain, totalBarsInCluster, minRange, maxRange, barsPadding);
return computeXScale(xDomain, totalBarsInCluster, minRange, maxRange, barsPadding, enableHistogramMode);
}
}

Expand Down Expand Up @@ -580,6 +592,7 @@ export function getAxisTicksPositions(
minMaxRanges.minRange,
minMaxRanges.maxRange,
barsPadding,
enableHistogramMode,
);

if (!scale) {
Expand Down
38 changes: 38 additions & 0 deletions src/lib/series/scales.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,44 @@ describe('Series scales', () => {
expect(scale.scale(3)).toBe(expectedBandwidth * 0);
});

describe('computeXScale with single value domain', () => {
const maxRange = 120;
const singleDomainValue = 3;
const minInterval = 1;

test('should return extended domain & range when in histogram mode', () => {
const xDomainSingleValue: XDomain = {
type: 'xDomain',
isBandScale: true,
domain: [singleDomainValue, singleDomainValue],
minInterval: minInterval,
scaleType: ScaleType.Linear,
};
const enableHistogramMode = true;

const scale = computeXScale(xDomainSingleValue, 1, 0, maxRange, 0, enableHistogramMode);
expect(scale.bandwidth).toBe(maxRange);
expect(scale.domain).toEqual([singleDomainValue, singleDomainValue + minInterval]);
expect(scale.range).toEqual([0, maxRange]);
});

test('should return unextended domain & range when not in histogram mode', () => {
const xDomainSingleValue: XDomain = {
type: 'xDomain',
isBandScale: true,
domain: [singleDomainValue, singleDomainValue],
minInterval: minInterval,
scaleType: ScaleType.Linear,
};
const enableHistogramMode = false;

const scale = computeXScale(xDomainSingleValue, 1, 0, maxRange, 0, enableHistogramMode);
expect(scale.bandwidth).toBe(maxRange);
expect(scale.domain).toEqual([singleDomainValue, singleDomainValue]);
expect(scale.range).toEqual([0, 0]);
});
});

test('should compute X Scale ordinal', () => {
const nonZeroGroupScale = computeXScale(xDomainOrdinal, 1, 120, 0);
const expectedBandwidth = 60;
Expand Down
25 changes: 19 additions & 6 deletions src/lib/series/scales.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export function computeXScale(
minRange: number,
maxRange: number,
barsPadding?: number,
enableHistogramMode?: boolean,
): Scale {
const { scaleType, minInterval, domain, isBandScale, timeZone } = xDomain;
const rangeDiff = Math.abs(maxRange - minRange);
Expand All @@ -60,20 +61,32 @@ export function computeXScale(
return new ScaleBand(domain, [minRange, maxRange], bandwidth, barsPadding);
} else {
if (isBandScale) {
const intervalCount = (domain[1] - domain[0]) / minInterval;
const bandwidth = rangeDiff / (intervalCount + 1);
const start = isInverse ? minRange - bandwidth : minRange;
const end = isInverse ? maxRange : maxRange - bandwidth;
return new ScaleContinuous(
const [domainMin, domainMax] = domain;
const isSingleValueHistogram = enableHistogramMode && domainMax - domainMin === 0;

const adjustedDomainMax = isSingleValueHistogram ? domainMin + minInterval : domainMax;
const adjustedDomain = [domainMin, adjustedDomainMax];

const intervalCount = (adjustedDomain[1] - adjustedDomain[0]) / minInterval;
const intervalCountOffest = isSingleValueHistogram ? 0 : 1;
const bandwidth = rangeDiff / (intervalCount + intervalCountOffest);

const rangeEndOffset = isSingleValueHistogram ? 0 : bandwidth;
const start = isInverse ? minRange - rangeEndOffset : minRange;
const end = isInverse ? maxRange : maxRange - rangeEndOffset;

markov00 marked this conversation as resolved.
Show resolved Hide resolved
const scale = new ScaleContinuous(
scaleType,
domain,
adjustedDomain,
[start, end],
bandwidth / totalBarsInCluster,
minInterval,
timeZone,
totalBarsInCluster,
barsPadding,
);

return scale;
} else {
return new ScaleContinuous(
scaleType,
Expand Down
1 change: 1 addition & 0 deletions src/state/chart_state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -836,6 +836,7 @@ export class ChartStore {
this.chartRotation,
this.chartTheme.axes,
barsPadding,
this.enableHistogramMode.get(),
);
if (dimensions) {
this.axesTicksDimensions.set(id, dimensions);
Expand Down
2 changes: 1 addition & 1 deletion src/state/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ export function computeSeriesGeometries(
const { stackedBarsInCluster, totalBarsInCluster } = countBarsInCluster(stacked, nonStacked);

// compute scales
const xScale = computeXScale(xDomain, totalBarsInCluster, 0, width, barsPadding);
const xScale = computeXScale(xDomain, totalBarsInCluster, 0, width, barsPadding, enableHistogramMode);
const yScales = computeYScales(yDomain, height, 0);

// compute colors
Expand Down
60 changes: 60 additions & 0 deletions stories/annotations.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -547,4 +547,64 @@ storiesOf('Annotations', module)
/>
</Chart>
);
})
.add('[test] line annotation single value histogram', () => {
const dataValues = [
{
dataValue: 3.5,
},
];

const style = {
line: {
strokeWidth: 3,
stroke: '#f00',
opacity: 1,
},
details: {
fontSize: 12,
fontFamily: 'Arial',
fontStyle: 'bold',
fill: 'gray',
padding: 0,
},
};

const chartRotation = select<Rotation>(
'chartRotation',
{
'0 deg': 0,
'90 deg': 90,
'-90 deg': -90,
'180 deg': 180,
},
0,
);

const xDomain = {
minInterval: 1,
};

return (
<Chart className={'story-chart'}>
<Settings debug={boolean('debug', false)} rotation={chartRotation} xDomain={xDomain} />
<LineAnnotation
annotationId={getAnnotationId('anno_1')}
domainType={AnnotationDomainTypes.XDomain}
dataValues={dataValues}
style={style}
/>
<Axis id={getAxisId('horizontal')} position={Position.Bottom} title={'x-domain axis'} />
<Axis id={getAxisId('vertical')} title={'y-domain axis'} position={Position.Left} />
<BarSeries
enableHistogramMode={true}
id={getSpecId('bars')}
xScaleType={ScaleType.Linear}
yScaleType={ScaleType.Linear}
xAccessor="x"
yAccessors={['y']}
data={[{ x: 3, y: 2 }]}
/>
</Chart>
);
});