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

Multicategory axis type #3300

Merged
merged 55 commits into from
Dec 11, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
e962fb4
big lint commit in axes.js
etpinard Nov 26, 2018
d49b7b4
first cut multicategory
etpinard Nov 26, 2018
d28ed18
rm useless `if(ax._mainLinePosition)` condition
etpinard Nov 27, 2018
9ebbe7d
generalize tick labels selection stash
etpinard Nov 27, 2018
6dbddc9
small fixup in axis drawTitle()
etpinard Nov 27, 2018
5720802
mv ax drawTitles() to end of Axes.drawOne
etpinard Nov 27, 2018
0f0661e
stash crispRound ax.gridwidth value on ax._gw
etpinard Nov 27, 2018
86f710c
2nd look at mutlicategory positioning
etpinard Nov 27, 2018
d986f94
adapt tickon:'boundaries' logic for multicategory axes
etpinard Nov 27, 2018
c680558
don't coerce ticktext/tickvals on multicategory axes
etpinard Nov 29, 2018
2a9ef87
mv maxRowLength to lib/array.js, use it it in xy defaults
etpinard Nov 29, 2018
f404d9f
add support for multicategory for box and violin traces
etpinard Nov 29, 2018
93323b8
add support for axis dividers
etpinard Nov 30, 2018
73f0365
:three: multicategory mocks
etpinard Nov 27, 2018
660a546
fix showdividers:false logic
etpinard Dec 4, 2018
1a4c95e
generalize Lib.maxRowLength
etpinard Nov 30, 2018
48df699
add a some potential TODOs
etpinard Dec 3, 2018
701f793
add Lib.minRowLength and use it to find trace._length
etpinard Dec 5, 2018
016846a
improve multicategory ax.makeCalcdata
etpinard Dec 5, 2018
3d097c2
initial support for multicategory on y-axes
etpinard Dec 5, 2018
d5777e9
fix data-referenced shapes on multicategory axes
etpinard Dec 5, 2018
b792b8d
add support for heatmap and contour traces on multicategoy axes
etpinard Dec 5, 2018
bf52cd7
add support for histogram* traces on multicategory axes
etpinard Dec 5, 2018
c3a0f5a
add support for finance traces on multicategory axes
etpinard Dec 5, 2018
80f4701
test xaxis.automargin:true + long multicategory labels
etpinard Dec 5, 2018
927c701
add multicategory axis auto-type tests
etpinard Dec 5, 2018
b49ca0c
fixup multicategory hover
etpinard Dec 5, 2018
e313064
add multicategory mock to mock lists
etpinard Dec 5, 2018
2bb1388
make tickons:'boundaries' the dflt on multicategory axes
etpinard Dec 6, 2018
3eac338
ignore bar base on (multi)category size axes
etpinard Dec 6, 2018
c802d32
fix box/violin x0/y0 multi-category edge case
etpinard Dec 6, 2018
7c3dd01
fix heatmap/contour x0/y0 + multicategory edge case
etpinard Dec 6, 2018
e0111d4
fix box/violin multicategory on value axis edge cases
etpinard Dec 6, 2018
69ccfd3
fix typed array support on axes with set type:multicategory
etpinard Dec 6, 2018
3746e03
aj-proof multicategory ax-autotype logic
etpinard Dec 6, 2018
cbb3b7d
add repositionOnUpdate opts for Axes.drawLabels
etpinard Dec 6, 2018
1965dae
rotate Drawing.bBox results in getLabelLevelSpan
etpinard Dec 7, 2018
ad97171
do not try to use ax.tickangle for secondary labels
etpinard Dec 7, 2018
a9eca09
accept typed array as inner arrays in multicategory autotype
etpinard Dec 11, 2018
3bc03e5
add explanation for why multicategory axes have no d2c & d2l
etpinard Dec 11, 2018
e71b7df
assert multicategory event data
etpinard Dec 11, 2018
5c85981
fix multicategory ax.cleanPos
etpinard Dec 11, 2018
627f24b
simplify histogram2d x/y len logic
etpinard Dec 11, 2018
a25c5e3
aj-proof heatmap_multicategory mock
etpinard Dec 11, 2018
858f7c2
fix #3255 - get automargin calls out of supplyDefaults
alexcjohnson Nov 20, 2018
bdabf48
refactor rangeslider pipeline
alexcjohnson Nov 20, 2018
b2a4b76
fixed range_slider_rangemode baseline
alexcjohnson Dec 11, 2018
177e6f0
use _replotting in cartesian dragbox
alexcjohnson Nov 28, 2018
da30f16
remove setScale categories fallback and update heatmap test comment
alexcjohnson Dec 11, 2018
68993c3
minor :palm_tree: for findMainSubplot
alexcjohnson Dec 11, 2018
f55e769
pre-collate counteraxes and subplot ids for each axis
alexcjohnson Dec 11, 2018
939df6e
Merge pull request #3323 from plotly/clean-defaults-mc
alexcjohnson Dec 11, 2018
5da3851
do not draw ticks above dividers
etpinard Dec 11, 2018
204b2e8
add multicategory + mirror ax mock
etpinard Dec 11, 2018
4b82426
do not accept number as box/violin x0 on multicategory axes
etpinard Dec 11, 2018
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
2 changes: 1 addition & 1 deletion src/components/legend/defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ module.exports = function legendDefaults(layoutIn, layoutOut, fullData) {
coerce('orientation');
if(containerOut.orientation === 'h') {
var xaxis = layoutIn.xaxis;
if(xaxis && xaxis.rangeslider && xaxis.rangeslider.visible) {
if(Registry.getComponentMethod('rangeslider', 'isVisible')(xaxis)) {
defaultX = 0;
defaultXAnchor = 'left';
defaultY = 1.1;
Expand Down
78 changes: 25 additions & 53 deletions src/components/rangeslider/draw.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,21 @@ var Color = require('../color');
var Titles = require('../titles');

var Cartesian = require('../../plots/cartesian');
var Axes = require('../../plots/cartesian/axes');
var axisIDs = require('../../plots/cartesian/axis_ids');

var dragElement = require('../dragelement');
var setCursor = require('../../lib/setcursor');

var constants = require('./constants');

module.exports = function(gd) {
var fullLayout = gd._fullLayout,
rangeSliderData = makeRangeSliderData(fullLayout);
var fullLayout = gd._fullLayout;
var rangeSliderData = fullLayout._rangeSliderData;
for(var i = 0; i < rangeSliderData.length; i++) {
var opts = rangeSliderData[i][constants.name];
// fullLayout._uid may not exist when we call makeData
opts._clipId = opts._id + '-' + fullLayout._uid;
}

/*
* <g container />
Expand All @@ -55,10 +60,6 @@ module.exports = function(gd) {
.selectAll('g.' + constants.containerClassName)
.data(rangeSliderData, keyFunction);

rangeSliders.enter().append('g')
.classed(constants.containerClassName, true)
.attr('pointer-events', 'all');

// remove exiting sliders and their corresponding clip paths
rangeSliders.exit().each(function(axisOpts) {
var opts = axisOpts[constants.name];
Expand All @@ -68,12 +69,16 @@ module.exports = function(gd) {
// return early if no range slider is visible
if(rangeSliderData.length === 0) return;

rangeSliders.enter().append('g')
.classed(constants.containerClassName, true)
.attr('pointer-events', 'all');

// for all present range sliders
rangeSliders.each(function(axisOpts) {
var rangeSlider = d3.select(this),
opts = axisOpts[constants.name],
oppAxisOpts = fullLayout[Axes.id2name(axisOpts.anchor)],
oppAxisRangeOpts = opts[Axes.id2name(axisOpts.anchor)];
var rangeSlider = d3.select(this);
var opts = axisOpts[constants.name];
var oppAxisOpts = fullLayout[axisIDs.id2name(axisOpts.anchor)];
var oppAxisRangeOpts = opts[axisIDs.id2name(axisOpts.anchor)];

// update range
// Expand slider range to the axis range
Expand All @@ -97,19 +102,9 @@ module.exports = function(gd) {
var domain = axisOpts.domain;
var tickHeight = (axisOpts._boundingBox || {}).height || 0;

var oppBottom = Infinity;
var subplotData = Axes.getSubplots(gd, axisOpts);
for(var i = 0; i < subplotData.length; i++) {
var oppAxis = Axes.getFromId(gd, subplotData[i].substr(subplotData[i].indexOf('y')));
oppBottom = Math.min(oppBottom, oppAxis.domain[0]);
}

opts._id = constants.name + axisOpts._id;
opts._clipId = opts._id + '-' + fullLayout._uid;
var oppBottom = opts._oppBottom;

opts._width = graphSize.w * (domain[1] - domain[0]);
opts._height = (fullLayout.height - margin.b - margin.t) * opts.thickness;
opts._offsetShift = Math.floor(opts.borderwidth / 2);

var x = Math.round(margin.l + (graphSize.w * domain[0]));

Expand Down Expand Up @@ -177,36 +172,9 @@ module.exports = function(gd) {
}
});
}

// update margins
Plots.autoMargin(gd, opts._id, {
x: domain[0],
y: oppBottom,
l: 0,
r: 0,
t: 0,
b: opts._height + margin.b + tickHeight,
pad: constants.extraPad + opts._offsetShift * 2
});
});
};

function makeRangeSliderData(fullLayout) {
var axes = Axes.list({ _fullLayout: fullLayout }, 'x', true),
name = constants.name,
out = [];

if(fullLayout._has('gl2d')) return out;

for(var i = 0; i < axes.length; i++) {
var ax = axes[i];

if(ax[name] && ax[name].visible) out.push(ax);
}

return out;
}

function setupDragElement(rangeSlider, gd, axisOpts, opts) {
var slideBox = rangeSlider.select('rect.' + constants.slideBoxClassName).node(),
grabAreaMin = rangeSlider.select('rect.' + constants.grabAreaMinClassName).node(),
Expand Down Expand Up @@ -393,11 +361,10 @@ function addClipPath(rangeSlider, gd, axisOpts, opts) {
}

function drawRangePlot(rangeSlider, gd, axisOpts, opts) {
var subplotData = Axes.getSubplots(gd, axisOpts),
calcData = gd.calcdata;
var calcData = gd.calcdata;

var rangePlots = rangeSlider.selectAll('g.' + constants.rangePlotClassName)
.data(subplotData, Lib.identity);
.data(axisOpts._subplotsWith, Lib.identity);

rangePlots.enter().append('g')
.attr('class', function(id) { return constants.rangePlotClassName + ' ' + id; })
Expand All @@ -413,7 +380,7 @@ function drawRangePlot(rangeSlider, gd, axisOpts, opts) {
var plotgroup = d3.select(this),
isMainPlot = (i === 0);

var oppAxisOpts = Axes.getFromId(gd, id, 'y'),
var oppAxisOpts = axisIDs.getFromId(gd, id, 'y'),
oppAxisName = oppAxisOpts._name,
oppAxisRangeOpts = opts[oppAxisName];

Expand Down Expand Up @@ -445,6 +412,11 @@ function drawRangePlot(rangeSlider, gd, axisOpts, opts) {
var xa = mockFigure._fullLayout.xaxis;
var ya = mockFigure._fullLayout[oppAxisName];

xa.clearCalc();
xa.setScale();
ya.clearCalc();
ya.setScale();

var plotinfo = {
id: id,
plotgroup: plotgroup,
Expand Down
67 changes: 67 additions & 0 deletions src/components/rangeslider/helpers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/**
* Copyright 2012-2018, Plotly, Inc.
* All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

'use strict';

var axisIDs = require('../../plots/cartesian/axis_ids');
var constants = require('./constants');
var name = constants.name;

function isVisible(ax) {
var rangeSlider = ax && ax[name];
return rangeSlider && rangeSlider.visible;
}
exports.isVisible = isVisible;

exports.makeData = function(fullLayout) {
var axes = axisIDs.list({ _fullLayout: fullLayout }, 'x', true);
var margin = fullLayout.margin;
var rangeSliderData = [];

if(!fullLayout._has('gl2d')) {
for(var i = 0; i < axes.length; i++) {
var ax = axes[i];

if(isVisible(ax)) {
rangeSliderData.push(ax);

var opts = ax[name];
opts._id = name + ax._id;
opts._height = (fullLayout.height - margin.b - margin.t) * opts.thickness;
opts._offsetShift = Math.floor(opts.borderwidth / 2);
}
}
}

fullLayout._rangeSliderData = rangeSliderData;
};

exports.autoMarginOpts = function(gd, ax) {
var opts = ax[name];

var oppBottom = Infinity;
var counterAxes = ax._counterAxes;
for(var j = 0; j < counterAxes.length; j++) {
var counterId = counterAxes[j];
var oppAxis = axisIDs.getFromId(gd, counterId);
oppBottom = Math.min(oppBottom, oppAxis.domain[0]);
}
opts._oppBottom = oppBottom;

var tickHeight = (ax.side === 'bottom' && ax._boundingBox.height) || 0;

return {
x: 0,
y: oppBottom,
l: 0,
r: 0,
t: 0,
b: opts._height + gd._fullLayout.margin.b + tickHeight,
pad: constants.extraPad + opts._offsetShift * 2
};
};
6 changes: 5 additions & 1 deletion src/components/rangeslider/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
var Lib = require('../../lib');
var attrs = require('./attributes');
var oppAxisAttrs = require('./oppaxis_attributes');
var helpers = require('./helpers');

module.exports = {
moduleType: 'component',
Expand All @@ -29,5 +30,8 @@ module.exports = {
layoutAttributes: require('./attributes'),
handleDefaults: require('./defaults'),
calcAutorange: require('./calc_autorange'),
draw: require('./draw')
draw: require('./draw'),
isVisible: helpers.isVisible,
makeData: helpers.makeData,
autoMarginOpts: helpers.autoMarginOpts
};
3 changes: 1 addition & 2 deletions src/components/shapes/calc_autorange.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 Down Expand Up @@ -84,7 +83,7 @@ function calcPaddingOptions(lineWidth, sizeMode, v0, v1, path, isYAxis) {
}

function shapeBounds(ax, v0, v1, path, paramsToUse) {
var convertVal = (ax.type === 'category') ? ax.r2c : ax.d2c;
var convertVal = (ax.type === 'category' || ax.type === 'multicategory') ? ax.r2c : ax.d2c;

if(v0 !== undefined) return [convertVal(v0), convertVal(v1)];
if(!path) return;
Expand Down
23 changes: 23 additions & 0 deletions src/lib/array.js
Original file line number Diff line number Diff line change
Expand Up @@ -132,3 +132,26 @@ exports.concat = function() {
}
return out;
};

exports.maxRowLength = function(z) {
return _rowLength(z, Math.max, 0);
};

exports.minRowLength = function(z) {
return _rowLength(z, Math.min, Infinity);
};

function _rowLength(z, fn, len0) {
if(isArrayOrTypedArray(z)) {
if(isArrayOrTypedArray(z[0])) {
var len = len0;
for(var i = 0; i < z.length; i++) {
len = fn(len, z[i].length);
}
return len;
} else {
return z.length;
}
}
return 0;
}
2 changes: 2 additions & 0 deletions src/lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ lib.isArrayOrTypedArray = arrayModule.isArrayOrTypedArray;
lib.isArray1D = arrayModule.isArray1D;
lib.ensureArray = arrayModule.ensureArray;
lib.concat = arrayModule.concat;
lib.maxRowLength = arrayModule.maxRowLength;
lib.minRowLength = arrayModule.minRowLength;

var modModule = require('./mod');
lib.mod = modModule.mod;
Expand Down
8 changes: 6 additions & 2 deletions src/plot_api/plot_api.js
Original file line number Diff line number Diff line change
Expand Up @@ -332,8 +332,7 @@ exports.plot = function(gd, data, layout, config) {
return Lib.syncOrAsync([
Registry.getComponentMethod('shapes', 'calcAutorange'),
Registry.getComponentMethod('annotations', 'calcAutorange'),
doAutoRangeAndConstraints,
Registry.getComponentMethod('rangeslider', 'calcAutorange')
doAutoRangeAndConstraints
], gd);
}

Expand All @@ -345,6 +344,11 @@ exports.plot = function(gd, data, layout, config) {
// store initial ranges *after* enforcing constraints, otherwise
// we will never look like we're at the initial ranges
if(graphWasEmpty) Axes.saveRangeInitial(gd);

// this one is different from shapes/annotations calcAutorange
// the others incorporate those components into ax._extremes,
// this one actually sets the ranges in rangesliders.
Registry.getComponentMethod('rangeslider', 'calcAutorange')(gd);
}

// draw ticks, titles, and calculate axis scaling (._b, ._m)
Expand Down
Loading