Skip to content

Commit

Permalink
fix: adjust domain & range for single value histogram (#265)
Browse files Browse the repository at this point in the history
  • Loading branch information
emmacunningham authored Jul 17, 2019
1 parent ebe9d3f commit 3f1358e
Show file tree
Hide file tree
Showing 6 changed files with 149 additions and 9 deletions.
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
40 changes: 34 additions & 6 deletions src/lib/series/scales.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,23 @@ export function countBarsInCluster(
};
}

function getBandScaleRange(
isInverse: boolean,
isSingleValueHistogram: boolean,
minRange: number,
maxRange: number,
bandwidth: number,
): {
start: number;
end: number;
} {
const rangeEndOffset = isSingleValueHistogram ? 0 : bandwidth;
const start = isInverse ? minRange - rangeEndOffset : minRange;
const end = isInverse ? maxRange : maxRange - rangeEndOffset;

return { start, end };
}

/**
* Compute the x scale used to align geometries to the x axis.
* @param xDomain the x domain
Expand All @@ -50,6 +67,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 +78,30 @@ 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 { start, end } = getBandScaleRange(isInverse, isSingleValueHistogram, minRange, maxRange, bandwidth);

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 @@ -840,6 +840,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 @@ -212,7 +212,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>
);
});

0 comments on commit 3f1358e

Please sign in to comment.