Skip to content

Ensure axis autorange is computed after transform operations #1260

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 5 commits into from
Jan 4, 2017
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
68 changes: 40 additions & 28 deletions src/plots/plots.js
Original file line number Diff line number Diff line change
Expand Up @@ -1921,8 +1921,9 @@ plots.transition = function(gd, data, layout, traces, frameOpts, transitionOpts)
plots.doCalcdata = function(gd, traces) {
var axList = Plotly.Axes.list(gd),
fullData = gd._fullData,
fullLayout = gd._fullLayout,
i, j;
fullLayout = gd._fullLayout;

var trace, _module, i, j;

// XXX: Is this correct? Needs a closer look so that *some* traces can be recomputed without
// *all* needing doCalcdata:
Expand Down Expand Up @@ -1951,46 +1952,57 @@ plots.doCalcdata = function(gd, traces) {
axList[i]._categories = axList[i]._initialCategories.slice();
}

// If traces were specified and this trace was not included,
// then transfer it over from the old calcdata:
for(i = 0; i < fullData.length; i++) {
// If traces were specified and this trace was not included, then transfer it over from
// the old calcdata:
if(Array.isArray(traces) && traces.indexOf(i) === -1) {
calcdata[i] = oldCalcdata[i];
Copy link
Collaborator

Choose a reason for hiding this comment

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

I know this part isn't new here, but... it isn't used anywhere, right? Looks to me like it was already broken by transforms (calcdata index and trace index aren't the same) and will be broken MORE by this PR (any trace not included will be dropped from autorange) so I'd remove it until we decide to do it right AND use it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah, is that a bug to be using that as an array index like that? If so, that may well be my fault. Unless I'm wrong, the intention was to reduce the work when animating. Use case: two traces. One contains 10MB of data, the other has two points. When animating the two points, it's best to avoid touching the 10MB trace. Perhaps we need to fix this.

Copy link
Collaborator

Choose a reason for hiding this comment

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

right, makes a lot of sense to want to do that. I guess for autorange we just need to store the various _min and _max entries in a different way so we can selectively replace only the ones for the traces we're recalculating. And trace index, depending on how this gets used (it's not yet, right?) you just have to be careful about what you mean by index, before or after transforms potentially insert new traces.

continue;
}
}

var trace = fullData[i],
cd = [];

// If traces were specified and this trace was not included, then transfer it over from
// the old calcdata:
if(Array.isArray(traces) && traces.indexOf(i) === -1) {
calcdata[i] = oldCalcdata[i];
continue;
}
var hasCalcTransform = false;

var _module;
if(trace.visible === true) {
// transform loop
for(i = 0; i < fullData.length; i++) {
trace = fullData[i];

// call calcTransform method if any
if(trace.transforms) {
if(trace.visible === true && trace.transforms) {
_module = trace._module;

// we need one round of trace module calc before
// the calc transform to 'fill in' the categories list
// used for example in the data-to-coordinate method
_module = trace._module;
if(_module && _module.calc) _module.calc(gd, trace);
// we need one round of trace module calc before
// the calc transform to 'fill in' the categories list
// used for example in the data-to-coordinate method
if(_module && _module.calc) _module.calc(gd, trace);

for(j = 0; j < trace.transforms.length; j++) {
var transform = trace.transforms[j];
for(j = 0; j < trace.transforms.length; j++) {
var transform = trace.transforms[j];

_module = transformsRegistry[transform.type];
if(_module && _module.calcTransform) {
_module.calcTransform(gd, trace, transform);
}
_module = transformsRegistry[transform.type];
if(_module && _module.calcTransform) {
hasCalcTransform = true;
_module.calcTransform(gd, trace, transform);
}
}
}
}

// clear stuff that should recomputed in 'regular' loop
if(hasCalcTransform) {
for(i = 0; i < axList.length; i++) {
axList[i]._min = [];
axList[i]._max = [];
axList[i]._categories = [];
}
}

// 'regular' loop
for(i = 0; i < fullData.length; i++) {
var cd = [];

trace = fullData[i];

if(trace.visible === true) {
_module = trace._module;
if(_module && _module.calc) cd = _module.calc(gd, trace);
}
Expand Down
55 changes: 55 additions & 0 deletions test/jasmine/tests/transform_filter_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ var createGraphDiv = require('../assets/create_graph_div');
var destroyGraphDiv = require('../assets/destroy_graph_div');
var assertDims = require('../assets/assert_dims');
var assertStyle = require('../assets/assert_style');
var customMatchers = require('../assets/custom_matchers');

describe('filter transforms defaults:', function() {

Expand Down Expand Up @@ -844,6 +845,10 @@ describe('filter transforms calc:', function() {
describe('filter transforms interactions', function() {
'use strict';

beforeAll(function() {
jasmine.addMatchers(customMatchers);
});

var mockData0 = [{
x: [-2, -1, -2, 0, 1, 2, 3],
y: [1, 2, 3, 1, 2, 3, 1],
Expand Down Expand Up @@ -900,6 +905,9 @@ describe('filter transforms interactions', function() {
assertUid(gd);
assertStyle(dims, ['rgb(255, 0, 0)'], [1]);

expect(gd._fullLayout.xaxis.range).toBeCloseToArray([0.87, 3.13]);
expect(gd._fullLayout.yaxis.range).toBeCloseToArray([0.85, 3.15]);

return Plotly.restyle(gd, 'marker.color', 'blue');
}).then(function() {
expect(gd._fullData[0].marker.color).toEqual('blue');
Expand All @@ -917,6 +925,9 @@ describe('filter transforms interactions', function() {
assertUid(gd);
assertStyle([1], ['rgb(255, 0, 0)'], [1]);

expect(gd._fullLayout.xaxis.range).toBeCloseToArray([2, 4]);
expect(gd._fullLayout.yaxis.range).toBeCloseToArray([0, 2]);

done();
});
});
Expand Down Expand Up @@ -1016,4 +1027,48 @@ describe('filter transforms interactions', function() {
})
.then(done);
});

it('should update axis categories', function(done) {
var data = [{
type: 'bar',
x: ['a', 'b', 'c', 'd', 'e', 'f', 'g'],
y: [1, 10, 100, 25, 50, -25, 100],
transforms: [{
type: 'filter',
operation: '<',
value: 10,
target: [1, 10, 100, 25, 50, -25, 100]
}]
}];

var gd = createGraphDiv();

Plotly.plot(gd, data).then(function() {
expect(gd._fullLayout.xaxis._categories).toEqual(['a', 'f']);
expect(gd._fullLayout.yaxis._categories).toEqual([]);

return Plotly.addTraces(gd, [{
type: 'bar',
x: ['h', 'i'],
y: [2, 1],
transforms: [{
type: 'filter',
operation: '=',
value: 'i'
}]
}]);
})
.then(function() {
expect(gd._fullLayout.xaxis._categories).toEqual(['a', 'f', 'i']);
expect(gd._fullLayout.yaxis._categories).toEqual([]);

return Plotly.deleteTraces(gd, [0]);
})
.then(function() {
expect(gd._fullLayout.xaxis._categories).toEqual(['i']);
expect(gd._fullLayout.yaxis._categories).toEqual([]);
})
.then(done);
});

});