Skip to content

Commit 9ae9084

Browse files
authored
Merge pull request #351 from xychang/master
Enable Stacked Bar Chart #enhancement
2 parents bdccf2b + fdea626 commit 9ae9084

5 files changed

Lines changed: 91 additions & 15 deletions

File tree

docs/InputParameters.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ These key-value pairs are placed under the root of the code block.
3737
| `ignoreAttachedValue` | Use `constValue` even if the target has a value attached on (true\|false) | 1~NT | false |
3838
| `ignoreZeroValue` | Treat zero value as missing (true\|false) | 1~NT | false |
3939
| `accum` | Accumulatively sum the values over time (true\|false) | 1~NT | false |
40+
| `stack` | Support stacked charts (true\|false) | 1 | false |
4041
| `penalty` | Value to use if the search target is missing on the day | 1~NT | |
4142
| `valueShift` | Amount to shift for each collected value | 1~NT | 0 |
4243
| `shiftOnlyValueLargerThan` | Do `valueShift` only if the value is larger then the specifed one | 1~NT | null |

examples/TestBarChart.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,4 +53,18 @@ bar:
5353
barColor: yellow, red, green, blue, orange, white
5454
```
5555

56+
57+
``` tracker
58+
searchType: tag
59+
searchTarget: sinsquare[0], sinsquare[1], sinsquare[2], sinsquare[3], sinsquare[4], sinsquare[5]
60+
folder: diary
61+
startDate: 2021-01-01
62+
endDate: 2021-01-05
63+
stack: true
64+
bar:
65+
title: Sin Square Wave (Stacked)
66+
yAxisLabel: Value
67+
yMin: 0
68+
barColor: yellow, red, green, blue, orange, black
69+
```
5670
Please also check those search targets in markdown files under folder 'diary'.

src/data.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,25 @@ export class Dataset implements IterableIterator<DataPoint> {
391391
}
392392
}
393393

394+
public shiftByDataset(shiftDataset: Dataset) {
395+
// Assume all datasets are of the same length
396+
for (let ind = 0; ind < this.values.length; ind++) {
397+
let currentValue = this.values[ind];
398+
if (shiftDataset.values[ind] !== null && currentValue !== null) {
399+
currentValue += shiftDataset.values[ind];
400+
} else if (shiftDataset.values[ind] !== null) {
401+
currentValue = shiftDataset.values[ind];
402+
}
403+
this.values[ind] = currentValue;
404+
if (currentValue < this.yMin) {
405+
this.yMin = currentValue;
406+
}
407+
if (currentValue > this.yMax) {
408+
this.yMax = currentValue;
409+
}
410+
}
411+
}
412+
394413
public getValues() {
395414
return this.values;
396415
}
@@ -571,6 +590,7 @@ export class RenderInfo {
571590
ignoreAttachedValue: boolean[];
572591
ignoreZeroValue: boolean[];
573592
accum: boolean[];
593+
stack: boolean;
574594
penalty: number[];
575595
valueShift: number[];
576596
shiftOnlyValueLargerThan: number[];
@@ -614,6 +634,7 @@ export class RenderInfo {
614634
this.ignoreAttachedValue = []; // false
615635
this.ignoreZeroValue = []; // false
616636
this.accum = []; // false, accum values start from zero over days
637+
this.stack = false;
617638
this.penalty = []; // null, use this value instead of null value
618639
this.valueShift = [];
619640
this.shiftOnlyValueLargerThan = [];

src/parsing.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1459,6 +1459,12 @@ export function getRenderInfoFromYaml(
14591459
renderInfo.accum = retAccum;
14601460
// console.log(renderInfo.accum);
14611461

1462+
// stack
1463+
if (typeof yaml.stack === "boolean") {
1464+
renderInfo.stack = yaml.stack;
1465+
}
1466+
// console.log(renderInfo.stack);
1467+
14621468
// penalty
14631469
let retPenalty = getNumberArrayFromInput(
14641470
"penalty",

src/rendering.ts

Lines changed: 49 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,18 @@ export function render(canvas: HTMLElement, renderInfo: RenderInfo) {
239239
dataset.accumulateValues();
240240
}
241241
}
242+
// stack
243+
if (renderInfo.stack) {
244+
// Traverse the datasets, and add up the values from each dataset.
245+
let lastDataset = null;
246+
for (let dataset of renderInfo.datasets) {
247+
if (dataset.getQuery().usedAsXDataset) continue;
248+
if (lastDataset) {
249+
dataset.shiftByDataset(lastDataset);
250+
}
251+
lastDataset = dataset;
252+
}
253+
}
242254

243255
for (let lineInfo of renderInfo.line) {
244256
let ret = renderLineChart(canvas, renderInfo, lineInfo);
@@ -905,16 +917,23 @@ function renderBar(
905917
// console.log(dataset);
906918
// console.log(barInfo);
907919
// console.log("%d/%d", currBarSet, totalNumOfBarSets);
908-
920+
909921
if (!renderInfo || !barInfo) return;
910922

911923
let barGap = 1;
912924
let barSetWidth = renderInfo.dataAreaSize.width / dataset.getLength();
913925
let barWidth = barSetWidth;
926+
let currentDiaplayInd = currBarSet;
927+
let totalDiaplaySet = totalNumOfBarSets;
914928
if (barSetWidth - barGap > 0) {
915929
barWidth = barSetWidth - barGap;
916930
}
917-
barWidth = barWidth / totalNumOfBarSets;
931+
if (!renderInfo.stack) {
932+
barWidth = barWidth / totalNumOfBarSets;
933+
} else {
934+
currentDiaplayInd = 0;
935+
totalDiaplaySet = 1;
936+
}
918937

919938
let portionLeft = (currBarSet + 1) / totalNumOfBarSets;
920939

@@ -936,28 +955,28 @@ function renderBar(
936955
.append("rect")
937956
.attr("x", function (p: DataPoint, i: number) {
938957
if (i === 0) {
939-
let portionVisible = currBarSet + 1 - totalNumOfBarSets / 2.0;
958+
let portionVisible = currentDiaplayInd + 1 - totalDiaplaySet / 2.0;
940959
if (portionVisible < 1.0) {
941960
return (
942961
chartElements.xScale(p.date) -
943962
barSetWidth / 2.0 +
944-
currBarSet * barWidth +
963+
currentDiaplayInd * barWidth +
945964
portionVisible * barWidth
946965
);
947966
}
948967
}
949968
return (
950969
chartElements.xScale(p.date) -
951970
barSetWidth / 2.0 +
952-
currBarSet * barWidth
971+
currentDiaplayInd * barWidth
953972
);
954973
})
955974
.attr("y", function (p: DataPoint) {
956975
return yScale(Math.max(p.value, 0));
957976
})
958977
.attr("width", function (p: DataPoint, i: number) {
959978
if (i === 0) {
960-
let portionVisible = currBarSet + 1 - totalNumOfBarSets / 2.0;
979+
let portionVisible = currentDiaplayInd + 1 - totalDiaplaySet / 2.0;
961980
if (portionVisible < 0.0) {
962981
return 0.0;
963982
} else if (portionVisible < 1.0) {
@@ -966,7 +985,7 @@ function renderBar(
966985
return barWidth;
967986
} else if (i === dataset.getLength() - 1) {
968987
let portionVisible =
969-
1.0 - (currBarSet + 1 - totalNumOfBarSets / 2.0);
988+
1.0 - (currentDiaplayInd + 1 - totalDiaplaySet / 2.0);
970989
if (portionVisible < 0.0) {
971990
return 0.0;
972991
} else if (portionVisible < 1.0) {
@@ -1626,15 +1645,30 @@ function renderBarChart(
16261645
let datasetOnLeftYAxis = [];
16271646
let datasetOnRightYAxis = [];
16281647
let xDatasetIds = renderInfo.datasets.getXDatasetIds();
1629-
for (let ind = 0; ind < barInfo.yAxisLocation.length; ind++) {
1630-
if (xDatasetIds.includes(ind)) continue;
1631-
let yAxisLocation = barInfo.yAxisLocation[ind];
1632-
if (yAxisLocation.toLowerCase() === "left") {
1633-
datasetOnLeftYAxis.push(ind);
1634-
} else if (yAxisLocation.toLocaleLowerCase() === "right") {
1635-
// right
1636-
datasetOnRightYAxis.push(ind);
1648+
if (renderInfo.stack) {
1649+
for (let ind = barInfo.yAxisLocation.length - 1; ind >= 0; ind--) {
1650+
if (xDatasetIds.includes(ind)) continue;
1651+
let yAxisLocation = barInfo.yAxisLocation[ind];
1652+
if (yAxisLocation.toLowerCase() === "left") {
1653+
datasetOnLeftYAxis.push(ind);
1654+
} else if (yAxisLocation.toLocaleLowerCase() === "right") {
1655+
// right
1656+
datasetOnRightYAxis.push(ind);
1657+
}
16371658
}
1659+
1660+
} else {
1661+
for (let ind = 0; ind < barInfo.yAxisLocation.length; ind++) {
1662+
if (xDatasetIds.includes(ind)) continue;
1663+
let yAxisLocation = barInfo.yAxisLocation[ind];
1664+
if (yAxisLocation.toLowerCase() === "left") {
1665+
datasetOnLeftYAxis.push(ind);
1666+
} else if (yAxisLocation.toLocaleLowerCase() === "right") {
1667+
// right
1668+
datasetOnRightYAxis.push(ind);
1669+
}
1670+
}
1671+
16381672
}
16391673

16401674
let retRenderLeftYAxis = renderYAxis(

0 commit comments

Comments
 (0)