-
-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Per-trace axis extremes #2849
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
Conversation
- 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.
src/plots/cartesian/autorange.js
Outdated
| } | ||
|
|
||
| function doAutoRange(ax) { | ||
| function concatExtremes(gd, ax, k) { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Improvements in -> 66df51e
src/plots/plots.js
Outdated
| for(i = 0; i < fullData.length; i++) { | ||
| trace = fullData[i]; | ||
|
|
||
| // find array attributes in trace |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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']; |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes -> d0699c0
src/plots/cartesian/axes.js
Outdated
| axes.getFromTrace = axisIds.getFromTrace; | ||
|
|
||
| var autorange = require('./autorange'); | ||
| axes.expand = autorange.expand; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔪
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔪 in d0699c0
src/plots/cartesian/constraints.js
Outdated
|
|
||
| for(k = 0; k < ax._min.length; k++) { | ||
| newVal = ax._min[k].val - getPad(ax._min[k]) / m; | ||
| var minArray = concatExtremes(gd, ax, 'min'); |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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]} | ||
| ]) |
There was a problem hiding this comment.
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)?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed and 🔒 in f1cda60
src/plots/cartesian/autorange.js
Outdated
| if(Registry.traceIs(trace, 'cartesian') || Registry.traceIs(trace, 'gl2d')) { | ||
| var axId = ax._id; | ||
| if(extremes[axId]) { | ||
| out = out.concat(extremes[axId][ext]); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
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
|
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 So, one way to possibly save this PR is to make |
- 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
|
Abandoned. Recycled commits in Abandoned. See #2860 |
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,
Axes.expandis replaced byAxes.findExtremes. To note, we no longer mutate_minand_maxarray 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. Thatextremesobject is slightly cumbersome, I'm open to suggestions to make it smoother to work with.Axes.getAutoRangenow must look for the_extremesobjects -- skipping thevisible:falseitems -- corresponding to a given axis and then computes the auto rangesetPositionswas skipped previously during 'plot' edits, we must now call it to make sure bar and box (and OHLC?) can restylevisiblecorrectly. I'll need to double-check a for more cases.concatExtremesdidn't pick the right values, fixed in 66df51eWe should now be able to change annotations and shapesreverted in 5cfee13 will be for later.visiblefrom an edit type 'calc' to 'plot' 🐎 done in -> 9a78013