-
-
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
Automargin fixes #2681
Automargin fixes #2681
Conversation
- Importantly, Plotly.react users probably were trashing these every time anyway resulting in bigger redraws than we needed. Now we're pulling them from the previous fullData if possible. - Also cleanData is now SOLELY for backward compatibility
Also fixes and tests a few traces that should have supported colorbars but didn't and some cases of editing colorbars that didn't previously work
|
||
// remove push margin object(s) | ||
if(rangeSliders.exit().size()) clearPushMargins(gd); | ||
}).remove(); |
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.
Ha exit().each()
returns the exit selection, good to know.
src/plot_api/plot_api.js
Outdated
doTicks, | ||
subroutines.drawData, | ||
subroutines.finalDraw, | ||
subroutines.drawMarginPushers |
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. That's unfortunate. Why do we need to call drawMarginPushers
on axis range relayouts?
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 Q, can't remember now why I needed that, I'll take another look at it...
// because the margins need to be fully determined before we can call | ||
// autorange and update axis ranges (which rangeselector needs to know which | ||
// button is active). Can we break out its automargin step from its draw step? | ||
Registry.getComponentMethod('rangeselector', 'draw')(gd); |
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.
Thanks for those 📚
src/traces/cone/colorbar.js
Outdated
Plots.autoMargin(gd, cbId); | ||
return; | ||
} | ||
if(!trace.showscale) return; |
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.
Where is the colorbar automargin push applied now?
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 the next commit -> fa86147 🤕
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.
No, it's in this commit - the autoMargin
I removed here is just the one to clear the entry for this colorbar, but that's not needed anymore due to plots.clearAutoMarginIds
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.
Thanks for headsup 👌
if(menus.exit().size()) clearPushMargins(gd); | ||
menus.exit().each(function() { | ||
// Most components don't need to explicitly remove autoMargin, because | ||
// marginPushers does this - but updatemenu updates don't go through |
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.
Why is this the case? Is it because updatemenus attributes are under the "arraydraw"
edit type? But then, in this case layout sliders should also have the same problem.
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.
Oh man, I missed sliders in this PR but sure enough, they still have the older pattern - thanks, I'll take a look at those.
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.
Updated sliders in 3deab4c
@@ -524,18 +524,36 @@ describe('legend relayout update', function() { | |||
afterEach(destroyGraphDiv); | |||
|
|||
it('should hide and show the legend', function(done) { | |||
var mockCopy = Lib.extendDeep({}, mock); |
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 this commit locking down fixes from this PR, or just improving our test case coverage?
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.
Pretty sure at least one of these was broken before this PR, but I don't recall which anymore.
* pre-calculated. | ||
*/ | ||
module.exports = function connectColorbar(gd, cd, moduleOpts) { | ||
if(typeof moduleOpts === 'function') return moduleOpts(gd, cd); |
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.
So, contour still gets its own version on module._colorbar
in
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.
... and that's the only exception?
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, contour and those that inherit from it (contourcarpet
, contourgl
, histogram2dcontour
).
I should note that there's one other place module.colorbar
gets used, that's in colorbar.draw
for getting the nestedProperty
string for title editing. If contour colorbars weren't at the module root we'd need to set a container
property of the function which would be a bit hacky... alternatively we could either a) make this function be module.colorbar.func
, or b) incorporate contour into this routine directly, triggered by some other property of module.colorbar
.
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.
alternatively we could either a) make this function be module.colorbar.func
This sounds cleaner to me. But no need to do this in this PR.
Registry.getComponentMethod('rangeselector', 'draw')(gd); | ||
Registry.getComponentMethod('sliders', 'draw')(gd); | ||
Registry.getComponentMethod('updatemenus', 'draw')(gd); | ||
// First reset the list of things that are allowed to change the margins |
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.
(for documentation purposes only) this patch is in function marginPushers() {}
src/plot_api/plot_api.js
Outdated
// TODO: shouldn't really need doAutoMargin here, but without it some | ||
// edits don't manage to tweak the margins the way they should | ||
// in the tests, the only one that presently breaks is axis.position | ||
Plots.doAutoMargin, |
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.
This is a little unfortunate. This could be a big performance hit. Would it be too much to ask to try to remove this in this PR?
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 only axis.position is affected, we could add a special (hopefully temporary) relayout flag to call Plots.doAutoMargin
after finalDraw
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.
I'll take a look again, didn't really understand why it was needed so I wasn't confident it was only ax.position
- that's just the only one that we test directly that triggered it. Anyway it's a very minor performance hit if the margins don't change at that point.
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.
Anyway it's a very minor performance hit if the margins don't change at that point.
Oh right, doAutoMargin
is smartTM like that. This additional doAutoMargin
isn't a big deal then.
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.
On further investigation, this doAutoMargin
call is definitely necessary for now. The reason is that axis automargin is not part of marginPushers
- they reserve the right to push the margins there now with Axes.allowAutoMargin
but the actual adjustment comes later during Axes.doTicks
.
As discussed above, this doAutoMargin
call won't actually redraw anything if the margins didn't change, so there's usually a minimal cost to this, but when the margins do change this triggers a full replot.
Long-term it would be far better to completely separate out margin calculations from drawing, so we never need to draw multiple times for a single change. But this addition doesn't represent a performance hit relative to the status quo.
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.
The reason is that axis automargin is not part of marginPushers - they reserve the right to push the margins there now with Axes.allowAutoMargin but the actual adjustment comes later during Axes.doTicks.
Thanks for writing this down! Could update that comment in the code above this line to reflect these new findings?
Long-term it would be far better to completely separate out margin calculations from drawing, so we never need to draw multiple times for a single change. But this addition doesn't represent a performance hit relative to the status quo.
The dream.
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.
Oh and about
Long-term it would be far better to completely separate out margin calculations from drawing, so we never need to draw multiple times for a single change. But this addition doesn't represent a performance hit relative to the status quo.
Is there an issue about this already? If not, we should open one.
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.
var colorway = layout.colorway || Color.defaults; | ||
var traceOut = {}, | ||
defaultColor = colorway[colorIndex % colorway.length]; | ||
var traceOut = {uid: uid}; |
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.
the above comment is non-blocking.
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.
yeah probably - but I thought it would be better in supplyDefaults
so we don't add another order-dependence to the plot
step - carpet dependents need this too. I think they're already required to plot
after the carpet for other reasons but still...
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.
but I thought it would be better in supplyDefaults so we don't add another order-dependence to the plot step - carpet dependents need this too.
Good point. Your way is 👌
@alexcjohnson Thanks for taking this on. Auto-margin and colorbars were very much in need of some ❤️ I'm a little concerned with:
My other comments aren't as important. |
Also includes a generalization of `Plots.autoMargin` so an object can have extent in plot fraction, as opposed to just position in plot fraction and extent in pixels. We could use this to allow for example colorbars to be sized based on the actual margins, not the pre-margin-pushing plot size as they are now, see note at colorbar/draw:119-124
var heightLessThan = opts.heightLessThan; | ||
|
||
var actualWidth = d3.select('.ygrid').node().getBoundingClientRect().width; | ||
var actualHeight = d3.select('.xgrid').node().getBoundingClientRect().height; |
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.
(non-blocking) but wouldn't d3.select('.subplot.xy > .plot').node().getBoundingClientRect
be more general (e.g. would work even on graphs with showgrid: false
)?
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, good point, but .plot
shrinks to the data, which may not reach the edges of the plot area (or could even go beyond with cliponaxis: false
). Possibly .bglayer .bg
though...
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.
or could even go beyond with cliponaxis: false
Yeah, good point here. Tough problem it turns out.
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.
* you can specify both edges as plot fractions in each dimension | ||
* l, r, t, b: the pixels to pad past the plot fraction x[l|r] and y[t|b] | ||
* pad: extra pixels to add in all directions, default 12 (why?) | ||
*/ |
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.
I mentioned this in the commit message:
Also includes a generalization of
Plots.autoMargin
so an object can
have extent in plot fraction, as opposed to just position in plot fraction
and extent in pixels. We could use this to allow for example colorbars
to be sized based on the actual margins, not the pre-margin-pushing plot size
as they are now, see note at colorbar/draw:119-124
but for more detail: colorbars (and possibly a few others?) when sized as plot fraction they use the initial margins to convert plot fraction to pixels - meaning that if the margins expand beyond that, a colorbar with length "1" would actually be taller than the plot area. This is probably fine if it's the colorbar itself that's expanding the margin - that means it's off-center and thus not aligned with the plot, so who really cares if it's a little bigger than the plot? But if something else (eg a rangeslider, or axis labels) expanded the margin, all of a sudden the colorbar is misaligned. Using yt/yb
we can size it correctly without getting into an infinite loop of automargin calls. Actually now that I think of the case ^^ which would be pretty ugly, I may try to do that in this PR as 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.
Thanks for the info 📚
and fix up size mode changes, along with a test that *actually* tests this instead of just looking like it tested it...
// if no explicit pad is given, use 12px unless there's a | ||
// specified margin that's smaller than that | ||
pad = Math.min(12, margin.l, margin.r, margin.t, margin.b); | ||
} |
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.
@etpinard seem reasonable? I started looking into this due to test failures at d02c76b - where some colorbars that were previously not able to expand the margin because they were internally pixel-sized suddenly became able to expand it when I switched it to plot-fraction-sized as described in #2681 (comment)... but then this 12px padding overrode user-supplied margins that were smaller - and that didn't seem right to me. Some of the baseline images with small explicit margins changed as a result.
The 12px here is an odd hard-coded feature - I suppose an alternative would be to make this an attribute in its own right (layout.margin.autopad
?) and put the min(12, margin.l...)
logic in supplyDefaults
.
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.
seem reasonable?
Very reasonable. 🥇
I suppose an alternative would be to make this an attribute in its own right (layout.margin.autopad?) and put the min(12, margin.l...) logic in supplyDefaults.
Maybe down the road, but no need in this PR.
must have been necessary mid-development then fixed later on?
src/components/sliders/draw.js
Outdated
delete sliderOpts._commandObserver; | ||
} | ||
|
||
Plots.autoMargin(gd, autoMarginId(sliderOpts)); |
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.
Could we add a comment similar to what's above https://github.com/plotly/plotly.js/pull/2681/files#r191879632 here?
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.
sure -> 230a782
src/components/colorbar/draw.js
Outdated
marginOpts.b = outerheight * bFrac; | ||
} | ||
else { | ||
marginOpts.t = marginOpts.b = 0; // TODO - not zero? |
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.
Could you expand on that TODO
comment?
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.
Ah thanks, that was just for me to check during development, it's correct as zeros -> removed in 230a782
Awesome 💃 |
Fixes #2651 - and a bunch of other things I encountered along the way:
trace.uid
back to the user input data, instead reusing the values fromoldFullData
if the user didn't specifyuid
in the trace. One more unexpected mutation gone 🎉 but more importantly,Plotly.react
users were probably trashing this anyway when making a new figure state, so they were triggering a more aggressive redraw than was really necessary.clearPushMargins
we used to need in rangesliders and updatemenus (and margin clearing by missing colorbars) are no longer necessary: we start a replot by clearing the list of allowed margin pusher ids, then delete disallowed ids when actually calculating the margins. Edits that don't go through the full replot can still adjust the margins by changing, adding, or deleting pushers, but they don't need to worry about deleting their old pushers on a full replot.The automargin system is still a mess in some fundamental ways that I didn't address here - really we need to break it out from the drawing steps of all of these components, so we can calculate the margins once and for all, then draw everything; as opposed to drawing, noticing we need to change the margins, then drawing all over again... but I think this is a step in the right direction.
cc @etpinard