Skip to content

Initial rangeslider ranges #473

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

Merged
merged 8 commits into from
Apr 26, 2016
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
16 changes: 16 additions & 0 deletions src/components/rangeslider/attributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,22 @@ module.exports = {
role: 'style',
description: 'Sets the border color of the range slider.'
},
range: {
valType: 'info_array',
role: 'info',
items: [
{valType: 'number'},
{valType: 'number'}
],
description: [
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@cldougl wording clear enough?

'Sets the range of the range slider.',
'If not set, defaults to the full xaxis range.',
'If the axis `type` is *log*, then you must take the',
'log of your desired range.',
'If the axis `type` is *date*, then you must convert',
'the date to unix time in milliseconds.'
].join(' ')
},
thickness: {
valType: 'number',
dflt: 0.15,
Expand Down
24 changes: 16 additions & 8 deletions src/components/rangeslider/create_slider.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@


var Plotly = require('../../plotly');
var Axes = require('../../plots/cartesian/axes');
var Lib = require('../../lib');

var svgNS = require('../../constants/xmlns_namespaces').svg;
Expand All @@ -18,7 +19,7 @@ var helpers = require('./helpers');
var rangePlot = require('./range_plot');


module.exports = function createSlider(gd, minStart, maxStart) {
module.exports = function createSlider(gd) {
var fullLayout = gd._fullLayout,
sliderContainer = fullLayout._infolayer.selectAll('g.range-slider'),
options = fullLayout.xaxis.rangeslider,
Expand All @@ -29,8 +30,8 @@ module.exports = function createSlider(gd, minStart, maxStart) {
x = fullLayout.margin.l,
y = fullLayout.height - height - fullLayout.margin.b;

minStart = minStart || 0;
maxStart = maxStart || width;
var minStart = 0,
maxStart = width;

var slider = document.createElementNS(svgNS, 'g');
helpers.setAttributes(slider, {
Expand Down Expand Up @@ -177,8 +178,8 @@ module.exports = function createSlider(gd, minStart, maxStart) {
min = min || -Infinity;
max = max || Infinity;

var rangeMin = fullLayout.xaxis.range[0],
rangeMax = fullLayout.xaxis.range[1],
var rangeMin = options.range[0],
rangeMax = options.range[1],
range = rangeMax - rangeMin,
pixelMin = (min - rangeMin) / range * width,
pixelMax = (max - rangeMin) / range * width;
Expand Down Expand Up @@ -217,9 +218,8 @@ module.exports = function createSlider(gd, minStart, maxStart) {
helpers.setAttributes(grabberMin, { 'transform': 'translate(' + (min - handleWidth - 1) + ')' });
helpers.setAttributes(grabberMax, { 'transform': 'translate(' + max + ')' });

// call to set range on plot here
var rangeMin = fullLayout.xaxis.range[0],
rangeMax = fullLayout.xaxis.range[1],
var rangeMin = options.range[0],
rangeMax = options.range[1],
range = rangeMax - rangeMin,
dataMin = min / width * range + rangeMin,
dataMax = max / width * range + rangeMin;
Expand All @@ -236,6 +236,11 @@ module.exports = function createSlider(gd, minStart, maxStart) {
}


// Set slider range using axis autorange if necessary.
if(!options.range) {
options.range = Axes.getAutoRange(fullLayout.xaxis);
}

var rangePlots = rangePlot(gd, width, height);

helpers.appendChildren(slider, [
Expand All @@ -248,6 +253,9 @@ module.exports = function createSlider(gd, minStart, maxStart) {
grabberMax
]);

// Set initially selected range
setRange(fullLayout.xaxis.range[0], fullLayout.xaxis.range[1]);

sliderContainer.data([0])
.enter().append(function() {
options.setRange = setRange;
Expand Down
16 changes: 14 additions & 2 deletions src/components/rangeslider/defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,23 @@ module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, axName, coun
attributes, attr, dflt);
}

coerce('visible');
coerce('thickness');
coerce('bgcolor');
coerce('bordercolor');
coerce('borderwidth');
coerce('thickness');
coerce('visible');
coerce('range');

// Expand slider range to the axis range
if(containerOut.range && !layoutOut[axName].autorange) {
var outRange = containerOut.range,
axRange = layoutOut[axName].range;

outRange[0] = Math.min(outRange[0], axRange[0]);
outRange[1] = Math.max(outRange[1], axRange[1]);
} else {
layoutOut[axName]._needsExpand = true;
}

if(containerOut.visible) {
counterAxes.forEach(function(ax) {
Expand Down
4 changes: 2 additions & 2 deletions src/components/rangeslider/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ module.exports = {
supplyLayoutDefaults: supplyLayoutDefaults
};

function draw(gd, minStart, maxStart) {
function draw(gd) {
if(!gd._fullLayout.xaxis) return;

var fullLayout = gd._fullLayout,
Expand All @@ -41,7 +41,7 @@ function draw(gd, minStart, maxStart) {
var height = (fullLayout.height - fullLayout.margin.b - fullLayout.margin.t) * options.thickness,
offsetShift = Math.floor(options.borderwidth / 2);

if(sliderContainer[0].length === 0 && !fullLayout._hasGL2D) createSlider(gd, minStart, maxStart);
if(sliderContainer[0].length === 0 && !fullLayout._hasGL2D) createSlider(gd);

// Need to default to 0 for when making gl plots
var bb = fullLayout.xaxis._boundingBox ?
Expand Down
15 changes: 9 additions & 6 deletions src/components/rangeslider/range_plot.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,12 @@ var svgNS = require('../../constants/xmlns_namespaces').svg;

module.exports = function rangePlot(gd, w, h) {

var traces = gd._fullData,
xaxis = gd._fullLayout.xaxis,
yaxis = gd._fullLayout.yaxis,
minX = xaxis.range[0],
maxX = xaxis.range[1],
var fullLayout = gd._fullLayout,
traces = gd._fullData,
xaxis = fullLayout.xaxis,
yaxis = fullLayout.yaxis,
minX = xaxis.rangeslider.range[0],
maxX = xaxis.rangeslider.range[1],
minY = yaxis.range[0],
maxY = yaxis.range[1];

Expand Down Expand Up @@ -62,7 +63,9 @@ module.exports = function rangePlot(gd, w, h) {
var posX = w * (x[k] - minX) / (maxX - minX),
posY = h * (1 - (y[k] - minY) / (maxY - minY));

pointPairs.push([posX, posY]);
if(!isNaN(posX) && !isNaN(posY)) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This will be relevant for #472. For the moment, this will guard for thrown errors when passing the pointPairs to makeScatter.

pointPairs.push([posX, posY]);
}
}

// more trace type range plots can be added here
Expand Down
6 changes: 5 additions & 1 deletion src/plot_api/plot_api.js
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,6 @@ Plotly.plot = function(gd, data, layout, config) {

function drawAxes() {
// draw ticks, titles, and calculate axis scaling (._b, ._m)
RangeSlider.draw(gd);
return Plotly.Axes.doTicks(gd, 'redraw');
}

Expand Down Expand Up @@ -310,6 +309,7 @@ Plotly.plot = function(gd, data, layout, config) {
Shapes.drawAll(gd);
Plotly.Annotations.drawAll(gd);
Legend.draw(gd);
RangeSlider.draw(gd);
RangeSelector.draw(gd);
}

Expand Down Expand Up @@ -2191,6 +2191,10 @@ Plotly.relayout = function relayout(gd, astr, val) {
docalc = true;
}

if(pleafPlus.indexOf('rangeslider') !== -1) {
docalc = true;
}

// toggling log without autorange: need to also recalculate ranges
// logical XOR (ie are we toggling log)
if(pleaf==='type' && ((parentFull.type === 'log') !== (vi === 'log'))) {
Expand Down
156 changes: 86 additions & 70 deletions src/plots/cartesian/axes.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,91 +110,106 @@ axes.minDtick = function(ax,newDiff,newFirst,allow) {
}
};

axes.doAutoRange = function(ax) {
if(!ax._length) ax.setScale();
axes.getAutoRange = function(ax) {
var newRange = [];

if(ax.autorange && ax._min && ax._max &&
ax._min.length && ax._max.length) {
var minmin = ax._min[0].val,
maxmax = ax._max[0].val,
i;

for(i = 1; i < ax._min.length; i++) {
if(minmin !== maxmax) break;
minmin = Math.min(minmin, ax._min[i].val);
}
for(i = 1; i < ax._max.length; i++) {
if(minmin !== maxmax) break;
maxmax = Math.max(maxmax, ax._max[i].val);
}

var j,minpt,maxpt,minbest,maxbest,dp,dv,
mbest = 0,
axReverse = (ax.range && ax.range[1]<ax.range[0]);
// one-time setting to easily reverse the axis
// when plotting from code
if(ax.autorange==='reversed') {
axReverse = true;
ax.autorange = true;
}
for(i=0; i<ax._min.length; i++) {
minpt = ax._min[i];
for(j=0; j<ax._max.length; j++) {
maxpt = ax._max[j];
dv = maxpt.val-minpt.val;
dp = ax._length-minpt.pad-maxpt.pad;
if(dv>0 && dp>0 && dv/dp > mbest) {
minbest = minpt;
maxbest = maxpt;
mbest = dv/dp;
}
var minmin = ax._min[0].val,
maxmax = ax._max[0].val,
i;

for(i = 1; i < ax._min.length; i++) {
if(minmin !== maxmax) break;
minmin = Math.min(minmin, ax._min[i].val);
}
for(i = 1; i < ax._max.length; i++) {
if(minmin !== maxmax) break;
maxmax = Math.max(maxmax, ax._max[i].val);
}

var j,minpt,maxpt,minbest,maxbest,dp,dv,
mbest = 0,
axReverse = (ax.range && ax.range[1]<ax.range[0]);

// one-time setting to easily reverse the axis
// when plotting from code
if(ax.autorange === 'reversed') {
axReverse = true;
ax.autorange = true;
}

for(i=0; i<ax._min.length; i++) {
minpt = ax._min[i];
for(j=0; j<ax._max.length; j++) {
maxpt = ax._max[j];
dv = maxpt.val-minpt.val;
dp = ax._length-minpt.pad-maxpt.pad;
if(dv>0 && dp>0 && dv/dp > mbest) {
minbest = minpt;
maxbest = maxpt;
mbest = dv/dp;
}
}
if(minmin===maxmax) {
ax.range = axReverse ?
[minmin+1, ax.rangemode!=='normal' ? 0 : minmin-1] :
[ax.rangemode!=='normal' ? 0 : minmin-1, minmin+1];
}
else if(mbest) {
if(ax.type==='linear' || ax.type==='-') {
if(ax.rangemode==='tozero' && minbest.val>=0) {
}

if(minmin === maxmax) {
newRange = axReverse ?
[minmin+1, ax.rangemode!=='normal' ? 0 : minmin-1] :
[ax.rangemode!=='normal' ? 0 : minmin-1, minmin+1];
}
else if(mbest) {
if(ax.type==='linear' || ax.type==='-') {
if(ax.rangemode==='tozero' && minbest.val>=0) {
minbest = {val: 0, pad: 0};
}
else if(ax.rangemode==='nonnegative') {
if(minbest.val - mbest*minbest.pad<0) {
minbest = {val: 0, pad: 0};
}
else if(ax.rangemode==='nonnegative') {
if(minbest.val - mbest*minbest.pad<0) {
minbest = {val: 0, pad: 0};
}
if(maxbest.val<0) {
maxbest = {val: 1, pad: 0};
}
if(maxbest.val<0) {
maxbest = {val: 1, pad: 0};
}

// in case it changed again...
mbest = (maxbest.val-minbest.val) /
(ax._length-minbest.pad-maxbest.pad);
}

ax.range = [
minbest.val - mbest*minbest.pad,
maxbest.val + mbest*maxbest.pad
];
// in case it changed again...
mbest = (maxbest.val-minbest.val) /
(ax._length-minbest.pad-maxbest.pad);
}

// don't let axis have zero size
if(ax.range[0]===ax.range[1]) {
ax.range = [ax.range[0]-1, ax.range[0]+1];
}
newRange = [
minbest.val - mbest*minbest.pad,
maxbest.val + mbest*maxbest.pad
];

// maintain reversal
if(axReverse) {
ax.range.reverse();
}
// don't let axis have zero size
if(newRange[0] === newRange[1]) {
newRange = [newRange[0]-1, newRange[0]+1];
}

// maintain reversal
if(axReverse) {
newRange.reverse();
}
}

return newRange;
};

axes.doAutoRange = function(ax) {
if(!ax._length) ax.setScale();

// TODO do we really need this?
var hasDeps = (ax._min && ax._max && ax._min.length && ax._max.length);

if(ax.autorange && hasDeps) {
ax.range = axes.getAutoRange(ax);

// doAutoRange will get called on fullLayout,
// but we want to report its results back to layout
var axIn = ax._gd.layout[ax._name];

if(!axIn) ax._gd.layout[ax._name] = axIn = {};
if(axIn!==ax) {

if(axIn !== ax) {
axIn.range = ax.range.slice();
axIn.autorange = ax.autorange;
}
Expand Down Expand Up @@ -241,7 +256,8 @@ axes.saveRangeInitial = function(gd, overwrite) {
// and make it a tight bound if possible
var FP_SAFE = Number.MAX_VALUE/2;
axes.expand = function(ax, data, options) {
if(!ax.autorange || !data) return;
// if(!(ax.autorange || (ax.rangeslider || {}).visible) || !data) return;
Copy link
Contributor

Choose a reason for hiding this comment

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

🐄 clean up comment.

if(!(ax.autorange || ax._needsExpand) || !data) return;
if(!ax._min) ax._min = [];
if(!ax._max) ax._max = [];
if(!options) options = {};
Expand Down
Binary file added test/image/baselines/range_slider_ranges.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading