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

Implement alignmentgroup and offsetgroup #3529

Merged
merged 5 commits into from
Feb 19, 2019
Merged
Show file tree
Hide file tree
Changes from all 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
10 changes: 10 additions & 0 deletions src/plots/cartesian/axis_ids.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,3 +114,13 @@ exports.idSort = function(id1, id2) {
if(letter1 !== letter2) return letter1 > letter2 ? 1 : -1;
return +(id1.substr(1) || 1) - +(id2.substr(1) || 1);
};

exports.getAxisGroup = function getAxisGroup(fullLayout, axId) {
var matchGroups = fullLayout._axisMatchGroups;

for(var i = 0; i < matchGroups.length; i++) {
var group = matchGroups[i];
if(group[axId]) return 'g' + i;
}
return axId;
};
2 changes: 2 additions & 0 deletions src/plots/plots.js
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,8 @@ plots.supplyDefaults = function(gd, opts) {
newFullLayout._scatterStackOpts = {};
// for the first scatter trace on each subplot (so it knows tonext->tozero)
newFullLayout._firstScatter = {};
// for grouped bar/box/violin trace to share config across traces
newFullLayout._alignmentOpts = {};

// for traces to request a default rangeslider on their x axes
// eg set `_requestRangeslider.x2 = true` for xaxis2
Expand Down
24 changes: 24 additions & 0 deletions src/traces/bar/attributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,30 @@ module.exports = {

marker: marker,

offsetgroup: {
valType: 'string',
role: 'info',
dflt: '',
editType: 'calc',
description: [
'Set several traces linked to the same position axis',
'or matching axes to the same',
'offsetgroup where bars of the same position coordinate will line up.'
].join(' ')
},
alignmentgroup: {
valType: 'string',
role: 'info',
dflt: '',
editType: 'calc',
description: [
'Set several traces linked to the same position axis',
'or matching axes to the same',
'alignmentgroup. This controls whether bars compute their positional',
'range dependently or independently.'
].join(' ')
},

selected: {
marker: {
opacity: scatterAttrs.selected.marker.opacity,
Expand Down
37 changes: 27 additions & 10 deletions src/traces/bar/cross_trace_calc.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ var BADNUM = require('../../constants/numerical').BADNUM;

var Registry = require('../../registry');
var Axes = require('../../plots/cartesian/axes');
var getAxisGroup = require('../../plots/cartesian/axis_ids').getAxisGroup;
var Sieve = require('./sieve.js');

/*
Expand Down Expand Up @@ -279,26 +280,42 @@ function setOffsetAndWidthInGroupMode(gd, pa, sieve) {
var distinctPositions = sieve.distinctPositions;
var minDiff = sieve.minDiff;
var calcTraces = sieve.traces;
var nTraces = calcTraces.length;

// if there aren't any overlapping positions,
// let them have full width even if mode is group
var overlap = (positions.length !== distinctPositions.length);

var nTraces = calcTraces.length;
var barGroupWidth = minDiff * (1 - bargap);
var barWidthPlusGap = (overlap) ? barGroupWidth / nTraces : barGroupWidth;
var barWidth = barWidthPlusGap * (1 - bargroupgap);

var groupId = getAxisGroup(fullLayout, pa._id) + calcTraces[0][0].trace.orientation;
var alignmentGroups = fullLayout._alignmentOpts[groupId] || {};

for(var i = 0; i < nTraces; i++) {
var calcTrace = calcTraces[i];
var t = calcTrace[0].t;
var trace = calcTrace[0].trace;

// computer bar group center and bar offset
var offsetFromCenter = overlap ?
((2 * i + 1 - nTraces) * barWidthPlusGap - barWidth) / 2 :
-barWidth / 2;
var alignmentGroupOpts = alignmentGroups[trace.alignmentgroup] || {};
var nOffsetGroups = Object.keys(alignmentGroupOpts.offsetGroups || {}).length;

// store bar width and offset for this trace
var barWidthPlusGap;
if(nOffsetGroups) {
barWidthPlusGap = barGroupWidth / nOffsetGroups;
} else {
barWidthPlusGap = overlap ? barGroupWidth / nTraces : barGroupWidth;
}

var barWidth = barWidthPlusGap * (1 - bargroupgap);

var offsetFromCenter;
if(nOffsetGroups) {
offsetFromCenter = ((2 * trace._offsetIndex + 1 - nOffsetGroups) * barWidthPlusGap - barWidth) / 2;
} else {
offsetFromCenter = overlap ?
((2 * i + 1 - nTraces) * barWidthPlusGap - barWidth) / 2 :
-barWidth / 2;
}

var t = calcTrace[0].t;
t.barwidth = barWidth;
t.poffset = offsetFromCenter;
t.bargroupwidth = barGroupWidth;
Expand Down
68 changes: 66 additions & 2 deletions src/traces/bar/defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
* LICENSE file in the root directory of this source tree.
*/


'use strict';

var Lib = require('../../lib');
Expand All @@ -15,9 +14,10 @@ var Registry = require('../../registry');

var handleXYDefaults = require('../scatter/xy_defaults');
var handleStyleDefaults = require('../bar/style_defaults');
var getAxisGroup = require('../../plots/cartesian/axis_ids').getAxisGroup;
var attributes = require('./attributes');

module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout) {
function supplyDefaults(traceIn, traceOut, defaultColor, layout) {
function coerce(attr, dflt) {
return Lib.coerce(traceIn, traceOut, attributes, attr, dflt);
}
Expand Down Expand Up @@ -78,4 +78,68 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout
errorBarsSupplyDefaults(traceIn, traceOut, lineColor || Color.defaultLine, {axis: 'x', inherit: 'y'});

Lib.coerceSelectionMarkerOpacity(traceOut, coerce);
}

function handleGroupingDefaults(traceIn, traceOut, fullLayout, coerce) {
var orientation = traceOut.orientation;
// N.B. grouping is done across all trace trace types that support it
var posAxId = traceOut[{v: 'x', h: 'y'}[orientation] + 'axis'];
var groupId = getAxisGroup(fullLayout, posAxId) + orientation;

var alignmentOpts = fullLayout._alignmentOpts || {};
var alignmentgroup = coerce('alignmentgroup');

var alignmentGroups = alignmentOpts[groupId];
if(!alignmentGroups) alignmentGroups = alignmentOpts[groupId] = {};

var alignmentGroupOpts = alignmentGroups[alignmentgroup];

if(alignmentGroupOpts) {
alignmentGroupOpts.traces.push(traceOut);
} else {
alignmentGroupOpts = alignmentGroups[alignmentgroup] = {
traces: [traceOut],
alignmentIndex: Object.keys(alignmentGroups).length,
offsetGroups: {}
};
}

var offsetgroup = coerce('offsetgroup');
var offsetGroups = alignmentGroupOpts.offsetGroups;
var offsetGroupOpts = offsetGroups[offsetgroup];

if(offsetgroup) {
if(!offsetGroupOpts) {
offsetGroupOpts = offsetGroups[offsetgroup] = {
offsetIndex: Object.keys(offsetGroups).length
};
}

traceOut._offsetIndex = offsetGroupOpts.offsetIndex;
}
}

function crossTraceDefaults(fullData, fullLayout) {
var traceIn, traceOut;

function coerce(attr) {
return Lib.coerce(traceOut._input, traceOut, attributes, attr);
}

for(var i = 0; i < fullData.length; i++) {
traceOut = fullData[i];

if(traceOut.type === 'bar') {
traceIn = traceOut._input;
if(fullLayout.barmode === 'group') {
handleGroupingDefaults(traceIn, traceOut, fullLayout, coerce);
}
}
}
}

module.exports = {
supplyDefaults: supplyDefaults,
crossTraceDefaults: crossTraceDefaults,
handleGroupingDefaults: handleGroupingDefaults
};
4 changes: 2 additions & 2 deletions src/traces/bar/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@
* LICENSE file in the root directory of this source tree.
*/


'use strict';

var Bar = {};

Bar.attributes = require('./attributes');
Bar.layoutAttributes = require('./layout_attributes');
Bar.supplyDefaults = require('./defaults');
Bar.supplyDefaults = require('./defaults').supplyDefaults;
Bar.crossTraceDefaults = require('./defaults').crossTraceDefaults;
Bar.supplyLayoutDefaults = require('./layout_defaults');
Bar.calc = require('./calc');
Bar.crossTraceCalc = require('./cross_trace_calc').crossTraceCalc;
Expand Down
2 changes: 0 additions & 2 deletions src/traces/bar/layout_defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
* LICENSE file in the root directory of this source tree.
*/


'use strict';

var Registry = require('../../registry');
Expand All @@ -15,7 +14,6 @@ var Lib = require('../../lib');

var layoutAttributes = require('./layout_attributes');


module.exports = function(layoutIn, layoutOut, fullData) {
function coerce(attr, dflt) {
return Lib.coerce(layoutIn, layoutOut, layoutAttributes, attr, dflt);
Expand Down
4 changes: 4 additions & 0 deletions src/traces/box/attributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
'use strict';

var scatterAttrs = require('../scatter/attributes');
var barAttrs = require('../bar/attributes');
var colorAttrs = require('../../components/color/attributes');
var extendFlat = require('../../lib/extend').extendFlat;

Expand Down Expand Up @@ -250,6 +251,9 @@ module.exports = {
},
fillcolor: scatterAttrs.fillcolor,

offsetgroup: barAttrs.offsetgroup,
alignmentgroup: barAttrs.alignmentgroup,

selected: {
marker: scatterAttrs.selected.marker,
editType: 'style'
Expand Down
29 changes: 21 additions & 8 deletions src/traces/box/cross_trace_calc.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

var Axes = require('../../plots/cartesian/axes');
var Lib = require('../../lib');
var getAxisGroup = require('../../plots/cartesian/axis_ids').getAxisGroup;

var orientations = ['v', 'h'];

Expand Down Expand Up @@ -51,9 +52,6 @@ function setPositionOffset(traceType, gd, boxList, posAxis) {
var axId = posAxis._id;
var axLetter = axId.charAt(0);

// N.B. reused in violin
var numKey = traceType === 'violin' ? '_numViolins' : '_numBoxes';

var i, j, calcTrace;
var pointList = [];
var shownPts = 0;
Expand All @@ -76,8 +74,9 @@ function setPositionOffset(traceType, gd, boxList, posAxis) {
// check for forced minimum dtick
Axes.minDtick(posAxis, boxdv.minDiff, boxdv.vals[0], true);

var num = fullLayout[numKey];
var group = (fullLayout[traceType + 'mode'] === 'group' && num > 1);
var numKey = traceType === 'violin' ? '_numViolins' : '_numBoxes';
var numTotal = fullLayout[numKey];
var group = fullLayout[traceType + 'mode'] === 'group' && numTotal > 1;
var groupFraction = 1 - fullLayout[traceType + 'gap'];
var groupGapFraction = 1 - fullLayout[traceType + 'groupgap'];

Expand All @@ -104,9 +103,23 @@ function setPositionOffset(traceType, gd, boxList, posAxis) {
bPos = 0;
} else {
dPos = dPos0;
bdPos = dPos * groupFraction * groupGapFraction / (group ? num : 1);
bPos = group ? 2 * dPos * (-0.5 + (t.num + 0.5) / num) * groupFraction : 0;
wHover = dPos * (group ? groupFraction / num : 1);

if(group) {
var groupId = getAxisGroup(fullLayout, posAxis._id) + trace.orientation;
var alignmentGroups = fullLayout._alignmentOpts[groupId] || {};
var alignmentGroupOpts = alignmentGroups[trace.alignmentgroup] || {};
var nOffsetGroups = Object.keys(alignmentGroupOpts.offsetGroups || {}).length;
var num = nOffsetGroups || numTotal;
var shift = nOffsetGroups ? trace._offsetIndex : t.num;

bdPos = dPos * groupFraction * groupGapFraction / num;
bPos = 2 * dPos * (-0.5 + (shift + 0.5) / num) * groupFraction;
wHover = dPos * groupFraction / num;
} else {
bdPos = dPos * groupFraction * groupGapFraction;
bPos = 0;
wHover = dPos;
}
}
t.dPos = dPos;
t.bPos = bPos;
Expand Down
24 changes: 23 additions & 1 deletion src/traces/box/defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
var Lib = require('../../lib');
var Registry = require('../../registry');
var Color = require('../../components/color');

var handleGroupingDefaults = require('../bar/defaults').handleGroupingDefaults;
var attributes = require('./attributes');

function supplyDefaults(traceIn, traceOut, defaultColor, layout) {
Expand Down Expand Up @@ -109,8 +109,30 @@ function handlePointsDefaults(traceIn, traceOut, coerce, opts) {
Lib.coerceSelectionMarkerOpacity(traceOut, coerce);
}

function crossTraceDefaults(fullData, fullLayout) {
var traceIn, traceOut;

function coerce(attr) {
return Lib.coerce(traceOut._input, traceOut, attributes, attr);
}

for(var i = 0; i < fullData.length; i++) {
traceOut = fullData[i];
var traceType = traceOut.type;

if(traceType === 'box' || traceType === 'violin') {
traceIn = traceOut._input;
if(fullLayout[traceType + 'mode'] === 'group') {
handleGroupingDefaults(traceIn, traceOut, fullLayout, coerce);
}
}
}
}

module.exports = {
supplyDefaults: supplyDefaults,
crossTraceDefaults: crossTraceDefaults,

handleSampleDefaults: handleSampleDefaults,
handlePointsDefaults: handlePointsDefaults
};
1 change: 1 addition & 0 deletions src/traces/box/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ var Box = {};
Box.attributes = require('./attributes');
Box.layoutAttributes = require('./layout_attributes');
Box.supplyDefaults = require('./defaults').supplyDefaults;
Box.crossTraceDefaults = require('./defaults').crossTraceDefaults;
Box.supplyLayoutDefaults = require('./layout_defaults').supplyLayoutDefaults;
Box.calc = require('./calc');
Box.crossTraceCalc = require('./cross_trace_calc').crossTraceCalc;
Expand Down
7 changes: 5 additions & 2 deletions src/traces/box/layout_defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,13 @@ var Lib = require('../../lib');
var layoutAttributes = require('./layout_attributes');

function _supply(layoutIn, layoutOut, fullData, coerce, traceType) {
var hasTraceType;
var category = traceType + 'Layout';
var hasTraceType = false;

for(var i = 0; i < fullData.length; i++) {
if(Registry.traceIs(fullData[i], category)) {
var trace = fullData[i];

if(Registry.traceIs(trace, category)) {
hasTraceType = true;
break;
}
Expand Down
3 changes: 3 additions & 0 deletions src/traces/histogram/attributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,9 @@ module.exports = {

marker: barAttrs.marker,

offsetgroup: barAttrs.offsetgroup,
alignmentgroup: barAttrs.alignmentgroup,

selected: barAttrs.selected,
unselected: barAttrs.unselected,

Expand Down
Loading