Skip to content

Commit

Permalink
fix: properly draw xAxis when selection larger than data
Browse files Browse the repository at this point in the history
  • Loading branch information
QuCMGisaia committed Jul 30, 2024
1 parent ee1c6f3 commit c85408a
Show file tree
Hide file tree
Showing 7 changed files with 131 additions and 51 deletions.
9 changes: 6 additions & 3 deletions src/histograms/AbstractHistogram.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,10 +140,13 @@ export abstract class AbstractHistogram {
}
}

/**
* Create the link between the scale of the chart and the width available
*/
protected getXDomainScale(rangeStart: number, rangeEnd: number): ScaleTime<number, number> | ScaleLinear<number, number> {
const scale = ((this.histogramParams.dataType === DataType.time) ?
(this.histogramParams.useUtc) ?
scaleUtc() : scaleTime() : scaleLinear()).range([rangeStart, rangeEnd]);
(this.histogramParams.useUtc ?
scaleUtc() : scaleTime()) : scaleLinear()).range([rangeStart, rangeEnd]);
return (scale instanceof Array ? scaleLinear(scale) : scale);
}

Expand Down Expand Up @@ -174,7 +177,7 @@ export abstract class AbstractHistogram {
}

protected getXDomainExtent(data: Array<HistogramData>, selectedStartValue: Date | number,
selectedEndValue: Date | number): Array<Date | number | { valueOf(): number; }> {
selectedEndValue: Date | number): Array<Date | number | { valueOf(): number; }> {
this.setDataInterval(data);
const xDomainExtent = new Array<Date | number | { valueOf(): number; }>();
const dataKeyUnionSelectedValues = new Array<Date | number>();
Expand Down
81 changes: 71 additions & 10 deletions src/histograms/charts/AbstractChart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,8 @@ export abstract class AbstractChart extends AbstractHistogram {
this.initializeDescriptionValues(minMaxBorders[0], minMaxBorders[1], this.histogramParams.bucketRange);
this.initializeChartDimensions();
this.customizeData(data);
this.createChartAxes(data);
const extendData = this.extendData(data);
this.createChartAxes(extendData);
this.drawChartAxes(this.chartAxes, 0);
this.plotChart(data);
this.showTooltips(data);
Expand Down Expand Up @@ -170,28 +171,34 @@ export abstract class AbstractChart extends AbstractHistogram {
if (this.brush?.isBrushed || this.brush?.isBrushing) {
return;
}
const axes = this.getAxes();

this.checkSelectedValuesValidity(selectedInputValues);
this.fromSetInterval = true;
const parsedSelectedValues = HistogramUtils.parseSelectedValues(selectedInputValues, this.histogramParams.dataType);
if (parsedSelectedValues.startvalue !== this.selectionInterval.startvalue ||
parsedSelectedValues.endvalue !== this.selectionInterval.endvalue) {
// Has the selection changed ?
if (parsedSelectedValues.startvalue !== this.selectionInterval.startvalue
|| parsedSelectedValues.endvalue !== this.selectionInterval.endvalue) {
// Set the new selection
this.selectionInterval.startvalue = parsedSelectedValues.startvalue;
this.selectionInterval.endvalue = parsedSelectedValues.endvalue;
const dataInterval = this.getDataInterval(<Array<HistogramData>>this.histogramParams.histogramData);
this.histogramParams.startValue = HistogramUtils.toString(this.selectionInterval.startvalue, this.histogramParams, dataInterval);
this.histogramParams.endValue = HistogramUtils.toString(this.selectionInterval.endvalue, this.histogramParams, dataInterval);
const data = this.dataDomain;
if (data !== null) {
if (HistogramUtils.isSelectionBeyondDataDomain(selectedInputValues, <Array<{ key: number; value: number; }>>data,
this.histogramParams.intervalSelectedMap)) {
// If beyond then outside of the scale of the axis
if (HistogramUtils.isSelectionBeyondDataDomain(selectedInputValues, data, this.histogramParams.intervalSelectedMap)) {
this.hasSelectionExceededData = true;
this.plot(<Array<{ key: number; value: number; }>>this.histogramParams.histogramData);
this.plot(this.histogramParams.histogramData);
} else {
if (this.hasSelectionExceededData) {
// Also check if there are no data beyond selection, then replot
// There for when the axis are resized due to selection being [0,0] when first plotting
if (this.hasSelectionExceededData
|| HistogramUtils.isDataDomainWithinSelection(selectedInputValues, data, this.histogramParams.intervalSelectedMap)) {
this.hasSelectionExceededData = false;
this.plot(<Array<{ key: number; value: number; }>>this.histogramParams.histogramData);
this.plot(this.histogramParams.histogramData);
}
const axes = this.getAxes();
const selectionBrushStart = Math.max(0, axes.xDomain(this.selectionInterval.startvalue));
const selectionBrushEnd = Math.min(axes.xDomain(this.selectionInterval.endvalue), this.chartDimensions.width);
if (this.brush) {
Expand Down Expand Up @@ -393,7 +400,9 @@ export abstract class AbstractChart extends AbstractHistogram {
this.chartAxes.stepWidth = 0;
const startRange = this.chartAxes.xDomain(data[0].key);
const endRange = this.chartAxes.xDomain(+data[data.length - 1].key + this.dataInterval);
this.chartAxes.xDataDomain = scaleBand().range([startRange, endRange]).paddingInner(0);
this.chartAxes.xDataDomain = scaleBand()
.range([startRange, endRange])
.paddingInner(0);
this.chartAxes.xDataDomain.domain(data.map((d) => (+d.key).toString()));
this.chartAxes.xAxis = axisBottom(this.chartAxes.xDomain).tickSize(0);
this.chartAxes.xTicksAxis = axisBottom(this.chartAxes.xDomain).ticks(this.histogramParams.xTicks).tickSize(this.minusSign * 4);
Expand Down Expand Up @@ -843,6 +852,58 @@ export abstract class AbstractChart extends AbstractHistogram {
}
}

/**
* When the selection is wider than the range of the data, adds "fake" buckets, for the x axis to be fully drawn
* @param data Data to plot
* @returns Extended data to fit the selection
*/
protected extendData(data: Array<HistogramData>): Array<HistogramData> {
if (this.selectionInterval.startvalue === this.selectionInterval.endvalue) {
return data;
}

const bucketSize = this.getDataInterval(data);
let extendedData = new Array<HistogramData>();
if (+data[0].key > +this.selectionInterval.startvalue) {
let fakeDataKey = +data[0].key;
while (fakeDataKey > +this.selectionInterval.startvalue) {
fakeDataKey = fakeDataKey - bucketSize;
if (this.histogramParams.dataType === DataType.numeric) {
extendedData.push({
key: fakeDataKey,
value: 0
});
} else {
extendedData.push({
key: new Date(fakeDataKey),
value: 0
});
}
}
}

extendedData = extendedData.concat(data);
if (+data[data.length - 1].key < +this.selectionInterval.endvalue) {
let fakeDataKey = +data[data.length - 1].key;
while (fakeDataKey < +this.selectionInterval.endvalue) {
fakeDataKey += +bucketSize;
if (this.histogramParams.dataType === DataType.numeric) {
extendedData.push({
key: fakeDataKey,
value: 0
});
} else {
extendedData.push({
key: new Date(fakeDataKey),
value: 0
});
}
}
}

return extendedData;
}

protected abstract plotChart(data: Array<HistogramData>): void;
protected abstract applyStyleOnSelection(): void;
protected abstract getStartPosition(data: Array<HistogramData>, index: number): number;
Expand Down
22 changes: 0 additions & 22 deletions src/histograms/charts/ChartBars.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,6 @@ export class ChartBars extends AbstractChart {
private strippedBarsContext: HistogramSVGG;
private headBandsContext: HistogramSVGG;

private minimumData = Number.MAX_VALUE;
private maximumData = Number.MIN_VALUE;
private minOffset = 0;
private maxOffset = 0;
public plot(inputData: Array<HistogramData>) {
super.plot(inputData);
}
Expand Down Expand Up @@ -115,11 +111,6 @@ export class ChartBars extends AbstractChart {
const minOffset = this.histogramParams.showStripes ? 0 : 0.1 * (maximum - minimum);
const maxOffset = 0.05 * (maximum - minimum);

this.maximumData = maximum;
this.minimumData = minimum;
this.maxOffset = maxOffset;
this.minOffset = minOffset;

let barsHeight = this.chartDimensions.height;
if (this.yStartsFromMin && this.histogramParams.showStripes) {
barsHeight = 0.9 * this.chartDimensions.height;
Expand Down Expand Up @@ -192,19 +183,6 @@ export class ChartBars extends AbstractChart {
this.chartAxes.stepWidth = this.chartAxes.xDomain(<number>data[0].key + this.dataInterval) - this.chartAxes.xDomain(data[0].key);
}
}

const ticksPeriod = Math.max(1, Math.round(data.length / this.histogramParams.xTicks));
const labelsPeriod = Math.max(1, Math.round(data.length / this.histogramParams.xLabels));
const labelPadding = (this.histogramParams.xAxisPosition === Position.bottom) ? 9 : -15;
// TO KEEP OR NOT ?
if (this.histogramParams.dataType === DataType.numeric) {
this.chartAxes.xTicksAxis = axisBottom(this.chartAxes.xDomain).tickValues(this.chartAxes.xDataDomain.domain()
.filter((d, i) => !(i % ticksPeriod)).map(d => Number(d))).tickSize(this.minusSign * 4);
this.chartAxes.xLabelsAxis = axisBottom(this.chartAxes.xDomain).tickSize(0).tickPadding(labelPadding)
.tickValues(this.chartAxes.xDataDomain.domain().filter((d, i) => !(i % labelsPeriod)).map(d => Number(d)))
.tickFormat(d => tickNumberFormat(d, this.histogramParams.numberFormatChar));
this.applyFormatOnXticks(data);
}
}

protected drawChartAxes(chartAxes: ChartAxes, leftOffset: number): void {
Expand Down
7 changes: 4 additions & 3 deletions src/histograms/charts/ChartCurve.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,11 @@ export class ChartCurve extends AbstractChart {
this.histogramParams.margin.right = 10;
}
this.initializeChartDimensions();
const extendedData = this.extendData(data);
if (chartIdToData.size === 1) {
// We add just one Y axis on the left
// No normalization
this.createChartXAxes(data);
this.createChartXAxes(extendedData);
this.createChartYLeftAxes(data);
this.drawChartAxes(this.chartAxes, 0);
this.drawYAxis(this.chartAxes, chartIdsToSides, Array.from(chartIds)[0]);
Expand All @@ -102,7 +103,7 @@ export class ChartCurve extends AbstractChart {
// We add on Y axis on right
// We add on Y axis on left
// No normalization
this.createChartXAxes(data);
this.createChartXAxes(extendedData);
if (!!this.histogramParams.mainChartId && chartIdToData.has(this.histogramParams.mainChartId)) {
this.createChartYRightAxes(dataArray[0]);
this.createChartYLeftAxes(dataArray[1]);
Expand Down Expand Up @@ -132,7 +133,7 @@ export class ChartCurve extends AbstractChart {
} else {
// No Y axis
// We normalize the data
this.createChartXAxes(data);
this.createChartXAxes(extendedData);
this.createChartNormalizeLeftAxes();
this.drawChartAxes(this.chartAxes, 0);
this.createClipperContext();
Expand Down
24 changes: 24 additions & 0 deletions src/histograms/utils/HistogramUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,30 @@ export class HistogramUtils {
}
}

public static isDataDomainWithinSelection(selectedInputValues: SelectedInputValues,
inputData: Array<HistogramData>,
intervalSelectedMap: Map<string, { values: SelectedOutputValues; x_position: number; }>): boolean {

let min = selectedInputValues.startvalue;
let max = selectedInputValues.endvalue;

intervalSelectedMap.forEach(values => {
if (min > values.values.startvalue) {
min = values.values.startvalue;
}

if (max < values.values.endvalue) {
max = values.values.endvalue;
}
});

if (inputData.length !== 0) {
return min <= inputData[0].key && max >= inputData[inputData.length - 1].key;
} else {
return true;
}
}

public static parseDataKey(inputData: Array<HistogramData>,
dataType: DataType): Array<HistogramData> {
if (dataType === DataType.time) {
Expand Down
36 changes: 23 additions & 13 deletions test/app.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ChartCurve, HistogramParams, ChartBars, SwimlaneBars, ChartArea, OneSelectionDonut, DonutParams } from '../dist/index.js'
import { ChartCurve, HistogramParams, ChartBars, SwimlaneBars, ChartArea, OneSelectionDonut, DonutParams, DataType } from '../dist/index.js'
import { Dimensions, Granularity, Margins, Timeline } from '../dist/index.js'


Expand Down Expand Up @@ -85,31 +85,41 @@ const defaultHistogramData = [
{ value: 120 + 300, key: 14000, chartId: '3' },
{ value: 156 + 340, key: 15000, chartId: '3' },
{ value: 156 + 300, key: 16000, chartId: '3' },

];

const startDate = 1616180400000;
const dt = 1000 * 3600 * 24;

const timeHistogramData = [];
for (let i=0; i<10; i++) {
timeHistogramData.push({ value: Math.random() * 300 - 100, key: new Date(startDate + i*dt), chartId: '1' });
}

const histogramBars = new ChartBars();
displayHistogram(histogramBars, 'containerBars')
displayHistogram(histogramBars, 'containerBars', defaultHistogramData);
const histogramCurve = new ChartCurve();
displayHistogram(histogramCurve, 'containerCurve')
displayHistogram(histogramCurve, 'containerCurve', defaultHistogramData, true);
const histogramArea = new ChartArea();
displayHistogram(histogramArea, 'containerArea')
displayHistogram(histogramArea, 'containerArea', defaultHistogramData);
const timeHistogramBars = new ChartBars();
displayHistogram(timeHistogramBars, 'containerHistTime', timeHistogramData, false, DataType.time)

function displayHistogram(histogram, containerName) {
function displayHistogram(histogram, containerName, data, selectionOverflow = false, dataType = DataType.numeric) {
histogram.histogramParams = histogramParams;
histogram.histogramParams.dataType = dataType;
histogram.histogramParams.multiselectable = true;
// histogram.histogramParams.selectionType = 'slider';
histogram.histogramParams.intervalSelectedMap = new Map();
histogram.histogramParams.histogramContainer = document.getElementById(containerName)
histogram.histogramParams.svgNode = document.getElementById(containerName).querySelector('svg');

histogram.selectionInterval = {
startvalue: defaultHistogramData[0].key,
endvalue: defaultHistogramData[defaultHistogramData.length - 1].key
};
histogram.histogramParams.histogramData = defaultHistogramData;
histogram.plot(defaultHistogramData);
histogram.resize(document.getElementById(containerName));
histogram.histogramParams.histogramData = data;
histogram.plot(data);

histogram.setSelectedInterval({
startvalue: data[0].key,
endvalue: selectionOverflow ? 10 * +data[data.length - 1].key : +data[data.length - 1].key
});
}

/** Timeline */
Expand Down
3 changes: 3 additions & 0 deletions test/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@
<div id="containerArea" style="width: 1000px; height: 150px;">
<svg id="svgixArea" style="width: 1000px; height: 150px"></svg>
</div>
<div id="containerHistTime" style="width: 1000px; height: 150px;">
<svg id="svgixHistTime" style="width: 1000px; height: 150px"></svg>
</div>
<div id="containerTimeline" style="width: 1000px; height: 150px;">
<svg id="svgixTimeline" style="width: 1000px; height: 150px"></svg>
</div>
Expand Down

0 comments on commit c85408a

Please sign in to comment.