Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
4 changes: 3 additions & 1 deletion src/traces/bar/layout_attributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@
module.exports = {
barmode: {
valType: 'enumerated',
values: ['stack', 'group', 'overlay'],
values: ['stack', 'group', 'overlay', 'relative'],
dflt: 'group',
role: 'info',
description: [
'Determines how bars at the same location coordinate',
'are displayed on the graph.',
'With *stack*, the bars are stacked on top of one another',
'With *relative*, the bars are stacked on top of one another,',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎉

'with negative values below the axis, positive values above',
'With *group*, the bars are plotted next to one another',
'centered around the shared location.',
'With *overlay*, the bars are plotted over one another,',
Expand Down
16 changes: 12 additions & 4 deletions src/traces/bar/set_positions.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,10 +116,11 @@ module.exports = function setPositions(gd, plotinfo) {
else barposition(bl);

var stack = (fullLayout.barmode === 'stack'),
relative = (fullLayout.barmode ==='relative'),
norm = fullLayout.barnorm;

// bar size range and stacking calculation
if(stack || norm) {
if(stack || relative || norm) {
// for stacked bars, we need to evaluate every step in every
// stack, because negative bars mean the extremes could be
// anywhere
Expand All @@ -142,13 +143,15 @@ module.exports = function setPositions(gd, plotinfo) {
ti = gd.calcdata[bl[i]];
for(j = 0; j < ti.length; j++) {
sv = Math.round(ti[j].p / sumround);
// store the negative sum value for p at the same key, with sign flipped
if (relative && ti[j].s < 0) sv = -Math.round(ti[j].p / sumround);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we could change this to sv = -sv. It's a super minor performance thing, but doesn't hurt and also makes it a bit more immediately clear that it's just the negative of the value.

var previousSum = sums[sv] || 0;
if(stack) ti[j].b = previousSum;
if(stack || relative) ti[j].b = previousSum;
barEnd = ti[j].b + ti[j].s;
sums[sv] = previousSum + ti[j].s;

// store the bar top in each calcdata item
if(stack) {
if(stack || relative) {
ti[j][sLetter] = barEnd;
if(!norm && isNumeric(sa.c2l(barEnd))) {
sMax = Math.max(sMax,barEnd);
Expand All @@ -161,13 +164,18 @@ module.exports = function setPositions(gd, plotinfo) {
if(norm) {
padded = false;
var top = norm==='fraction' ? 1 : 100,
isNegative = false,
tiny = top/1e9; // in case of rounding error in sum
sMin = 0;
sMax = stack ? top : 0;
for(i = 0; i < bl.length; i++) { // trace index
ti = gd.calcdata[bl[i]];
for(j = 0; j < ti.length; j++) {
scale = top / sums[Math.round(ti[j].p/sumround)];
relAndNegative = relative && ti[j].s < 0;
sv = Math.round(ti[j].p/sumround);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🐄 spacing around the division /.

if (relAndNegative) sv = -Math.round(ti[j].p/sumround); // locate negative sum amount for this p val
scale = top / sums[sv];
if (relAndNegative) scale *= -1; // preserve sign if negative
ti[j].b *= scale;
ti[j].s *= scale;
barEnd = ti[j].b + ti[j].s;
Expand Down
13 changes: 13 additions & 0 deletions test/image/mocks/bar_stackrelative_negative.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"data":[
{"name":"Col1","y":["-1","2","3","4","5"],"x":["1","2","3","4","5"],"type":"bar","uid":"aeb9ea"},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🐄 Fix the alignment to be typical prettyprinted json. Single-line objects are fine if they fit in < 80 characters, but otherwise break them up.

{"name":"Col2","y":["2","3","4","-3","2"],"x":["1","2","3","4","5"],"type":"bar","uid":"2f201d"},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, the uid should likely be removed from the mock - it's generated per-plot and we should keep mocks as lean as possible.

{"name":"Col3","y":["5","4","3","-2","1"],"x":["1","2","3","4","5"],"type":"bar","uid":"aef0bf"},
{"name":"Col4","y":["-3","0","1","0","-3"],"x":["1","2","3","4","5"],"type":"bar","uid":"330b4d"}
],
"layout":{
"height":400,
"width":400,
"barmode":"relative"
}
}
14 changes: 14 additions & 0 deletions test/image/mocks/bar_stackrelativeto100_negative.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"data":[
{"name":"Col1","y":["-1","2","3","4","5"],"x":["1","2","3","4","5"],"type":"bar","uid":"aeb9ea"},
{"name":"Col2","y":["2","3","4","-3","2"],"x":["1","2","3","4","5"],"type":"bar","uid":"2f201d"},
{"name":"Col3","y":["5","4","3","-2","1"],"x":["1","2","3","4","5"],"type":"bar","uid":"aef0bf"},
{"name":"Col4","y":["-3","0","1","0","-3"],"x":["1","2","3","4","5"],"type":"bar","uid":"330b4d"}
],
"layout":{
"height":400,
"width":400,
"barmode":"relative",
"barnorm":"percent"
}
}