-
-
Notifications
You must be signed in to change notification settings - Fork 1.9k
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
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
@@ -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) { |
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
@@ -50,6 +50,7 @@ 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
@@ -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'); |
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.
test/jasmine/tests/bar_test.js
Outdated
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.expand
is replaced byAxes.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. Thatextremes
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 thevisible:false
items -- corresponding to a given axis and then computes the auto rangesetPositions
was skipped previously during 'plot' edits, we must now call it to make sure bar and box (and OHLC?) can restylevisible
correctly. I'll need to double-check a for more cases.concatExtremes
didn't pick the right values, fixed in 66df51eWe should now be able to change annotations and shapesreverted in 5cfee13 will be for later.visible
from an edit type 'calc' to 'plot' 🐎 done in -> 9a78013