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

Per-trace axis extremes #2849

Closed
wants to merge 26 commits into from

Conversation

etpinard
Copy link
Contributor

@etpinard etpinard commented Jul 26, 2018

to be merged in #2837

By trying to address @alexcjohnson 's #2837 (comment), I opened up a pretty big can of worms. There are twice as many commits here than in #2837 😱

Oh well, I think this is for the best. In brief,

  • 67db333 adds a test that fails in Faster trace visibility toggling #2837
  • Axes.expand is replaced by Axes.findExtremes. To note, we no longer mutate _min and _max array into the axis to expand, but instead we fill in _extremes: {x: {min: [], max: [], y: {min: [], max: []}} objects in the traces and layout components that can expand the axis range. That extremes object is slightly cumbersome, I'm open to suggestions to make it smoother to work with.
  • Axes.getAutoRange now must look for the _extremes objects -- skipping the visible:false items -- corresponding to a given axis and then computes the auto range
  • 4c250c3 is very important, setPositions was skipped previously during 'plot' edits, we must now call it to make sure bar and box (and OHLC?) can restyle visible correctly. I'll need to double-check a for more cases.
  • one polar mock is currently failing, but I think it is more correct now. Update: the baseline is correct, the old concatExtremes didn't pick the right values, fixed in 66df51e
  • We should now be able to change annotations and shapes visible from an edit type 'calc' to 'plot' 🐎 done in -> 9a78013 reverted in 5cfee13 will be for later.

- a Axes.expend clone that does not append things to ax._min/ax._max,
  but instead returns two arrays, a min array and max array
  of potential data extremes
- to be filled with the results of findExtremes
- pass gd, to look for _extremes in gd._fullData and eventually
  in layout container to can expand the axis ranges
- N.B. sploms are linked to multiple axes per traces, so we can't
  rely on single 'x' and 'y' in _extremes ->  use ax._id instead!
- here we append the min/max arrays of the corresponding trace
- questionable commit, especially the part in autorange.js,
  but this doesn't make any test fail ?!?
- N.B. polar is the only subplot apart from cartesian that
  used Axes.expand and friends.
@etpinard etpinard added this to the v1.40.0 milestone Jul 26, 2018
@@ -174,15 +178,40 @@ function makePadFn(ax) {
return function getPad(pt) { return pt.pad + (pt.extrapad ? extrappad : 0); };
}

function doAutoRange(ax) {
function concatExtremes(gd, ax, k) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hmm. Come to think of it, this thing will be a drag. Looping over all traces, annotations and shapes for every axis, doesn't scale very well.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Improvements in -> 66df51e

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

// find array attributes in trace
Copy link
Collaborator

Choose a reason for hiding this comment

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

🤔 isn't PlotSchema.findArrayAttributes(trace) self-documenting enough?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

good call -> d0699c0

var apos = ann['a' + letter];
var ref = ann[letter + 'ref'];
var aref = ann['a' + letter + 'ref'];
var shift = ann[letter + 'shift'];
Copy link
Collaborator

Choose a reason for hiding this comment

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

if you did * (letter === 'x' ? 1 : -1) the switch below would collapse.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yes -> d0699c0

@@ -50,6 +50,7 @@ axes.getFromTrace = axisIds.getFromTrace;
var autorange = require('./autorange');
axes.expand = autorange.expand;
Copy link
Collaborator

Choose a reason for hiding this comment

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

🔪

Copy link
Contributor Author

Choose a reason for hiding this comment

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

🔪 in d0699c0

@@ -143,15 +144,17 @@ exports.enforce = function enforceAxisConstraints(gd) {
var newVal;
var k;

for(k = 0; k < ax._min.length; k++) {
newVal = ax._min[k].val - getPad(ax._min[k]) / m;
var minArray = concatExtremes(gd, ax, 'min');
Copy link
Collaborator

Choose a reason for hiding this comment

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

Hmm, so looks like we can call concatExtremes twice with the same results? Could we cache? Like, clear it maybe during supplyDefaults, then in concatExtremes first look for the cached value before calculating it and setting the cache?

Copy link
Contributor Author

@etpinard etpinard Jul 27, 2018

Choose a reason for hiding this comment

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

Some improvements in 66df51e. concatExtremes is still called twice on graphs with constrained axes, so would do better if we want.

Plotly.plot(gd, [
{type: 'bar', x: [1, 2, 3], y: [1, 2, 1]},
{type: 'bar', x: [1, 2, 3], y: [-1, -2, -1]}
])
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is there a test with stacked bars, where the extremes of one trace depend on the traces before it (so may need to be recalculated if an earlier trace changes visibility)?

Copy link
Contributor Author

@etpinard etpinard Jul 27, 2018

Choose a reason for hiding this comment

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

Is there a test with stacked bars,

Thanks for writing this down. Visibility restyles are broken on stacked bars, but for a different reason: Bar.setPositions mutates calcdata[i][j].b which is first set in Bar.calc 🙃

where the extremes of one trace depend on the traces before it

Yep, that's exactly why commit 4c250c3 is very important, 'plot' edit types now call setPositions for this reason.

Copy link
Contributor Author

@etpinard etpinard Jul 27, 2018

Choose a reason for hiding this comment

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

fixed and 🔒 in f1cda60

if(Registry.traceIs(trace, 'cartesian') || Registry.traceIs(trace, 'gl2d')) {
var axId = ax._id;
if(extremes[axId]) {
out = out.concat(extremes[axId][ext]);
Copy link
Collaborator

Choose a reason for hiding this comment

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

In principle we can collapse out more than just concat - new items can be mooted or can moot existing items based on the three criteria used in expand/findExtremes

I suppose it's worth testing with some real examples though - doAutoRange scales as O(min.length * max.length) which says it's a good idea to minimize the length of the min and max arrays, but excluding items from the min and max arrays scales as something like O(finalArray.length * uncompressedTotalLength) which is also sort of quadratic... not exactly a clear win either way.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Actually there’s a probably much more common case where I’m pretty sure it’s a big win to collapse more here: lots of traces each with only one or two min/max entries. Like 100 line or bar traces, or 100 marker traces with constant marker size. Collapsing here takes ~100 * 2 comparisons, then doAutorange only has one calculation to do, whereas simple concat here leaves 100 * 100 calculations for doAutorange

@alexcjohnson
Copy link
Collaborator

one polar mock is currently failing, but I think it is more correct now.

I think I agree - the new one (radial autorange still happens even though angular data is invalid) matches how we handle cartesian with invalid data on the opposite axis, right?

... to keep track of traces, annotations and shapes plotted on
    axis ax. Adapt concatExtremes accordingly!
- Bar.setPositions mutate 'b' in bar trace calcdate, we need
  to reinit 'b' during setPositions so that we can skip
  calc on visible edits
@etpinard
Copy link
Contributor Author

I'm starting to think we should abandon this PR and #2837 😢

Consider

Plotly.newPlot(gd, [{
  visible: false,
  y: [1, 2, 1]
}])

Plotly.restyle(gd, 'visible', true)

on this branch, this snippet shows a blank graph. That's because data arrays aren't coerced for visible:false traces and _module.calc is skipped for visible!==true traces.

So, one way to possibly save this PR is to make _module.calc run on 'legendonly' traces (note that data arrays are coerced for 'legendonly' traces) and make visible restyle calls on visible:false traces go through the 'calc' edit pipeline. Performance improvements would still be detected on legend toggling (where we toggle between visible 'legendonly' and true) - which is really the main purpose of this PR.

- we would need to coerce their xref and yref
  even when `visible:false`. Not worth the effort at
  the moment.

This reverts commit 9a78013.
... to handle `visible:false` annotations and shapes that
    do not go through calcAutorange at the moment.
... by factoring out logic from findExtremes
@etpinard
Copy link
Contributor Author

Abandoned. Recycled commits in Abandoned. See #2860

@etpinard etpinard closed this Jul 31, 2018
@etpinard etpinard deleted the per-trace-ax-extremes branch July 31, 2018 19:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants