-
-
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
automatic margins for long labels #2243
Conversation
Also note that I didn't test this with subplots, I wanted to kick off some discussion first :) |
This looks like a very solid first step. 💪
I think so. I'd vote for something like This new attribute will require some fancy We should also test various combinations of things that can push margins e.g. colorbars, legend, range sliders, update menus and layout sliders (anything else?). Working this patch in One final comment, should |
Re the testing with other things that push margins, it behaves just as detailed in #1199 ... no improvement but no worse either. The margins move such that there is enough room for the labels, and then the legend still sits on top of the labels. So I would consider that a separate issue that we can tackle as part of solving #1199 etc? |
@@ -50,6 +50,8 @@ module.exports = function handleTickLabelDefaults(containerIn, containerOut, coe | |||
} | |||
|
|||
if(axType !== 'category' && !options.noHover) coerce('hoverformat'); | |||
|
|||
coerce('ticklabelsautomargin'); |
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.
@nicolaskruchten you'll have to add an option to opt-out of coercing ticklabelautomargin
in handleTickLabelDefaults
so that other callees
don't cough on it.
Thanks for the hint @etpinard ! Now I can't figure out why this PR is breaking all the |
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.
@nicolaskruchten the gl3d image tests are now passing on CI after clearing the cache (i.e. rm node_modules/
). The image tests are still failing as they are looking for the corresponding baseline PNG for your long_axis_labels.json
mock.
You might want to try generating that baseline image locally. The instructions are here. Let me know if you need help.
@@ -50,6 +50,8 @@ module.exports = function handleTickLabelDefaults(containerIn, containerOut, coe | |||
} | |||
|
|||
if(axType !== 'category' && !options.noHover) coerce('hoverformat'); | |||
|
|||
if(axType === 'cartesian') coerce('ticklabelsautomargin'); |
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 this won't work. axType
is either 'linear', 'date', 'log' or 'category'.
I think the easiest way would to add an options.automargin
flag set to true
when called from plots/cartesian/axis_defaults.js
.
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 does coerce('ticklabelsautomargin')
have an effect when showticklabels
is false? More precisely, do the axis title font settings and/or ticklen
affect the auto-margin computations?
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'm not sure what coerce
actually does, to be honest, so I'm not sure if it will have an impact. What exactly does it do? Is it necessary here?
To answer your question more generally, the auto-margin calculation explicitly takes the axis title font size into account, and for the rest relies on the pre-existing bounding-box calculation.
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'm not sure what coerce actually does,
In brief, coerce
fills in fullLayout
with a sanitise version of its user layout value. Almost everywhere downstream of the Plots.supplyDefaults
uses fullLayout
and fullData
value (i.e. not gd.layout
and gd.data
) including your new logic in axes.js
.
So, yes it is necessary 😄
the auto-margin calculation explicitly takes the axis title font size into account,
Ok great. So, auto-margin doesn't just depend on tick settings like the attribute name ticklabelsautomargin
is suggesting. We'll need to think about how to name this new attribute some more cc #2243 (comment)
for the rest relies on the pre-existing bounding-box calculation.
Ok, so this should depend on ticklen
(we should double-check though). Knowing which attributes impact the auto-margin computation is important to make sure relayout calls like Plotly.relayout(gd, 'xaxis.ticklen', /**/)
update the (auto) margin when ticklabelsautomargin
is turned on.
@@ -344,6 +344,16 @@ module.exports = { | |||
editType: 'ticks', | |||
description: 'Determines whether or not the tick labels are drawn.' | |||
}, | |||
ticklabelsautomargin: { |
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 might be too verbose. Would (x|y)axis.automargin
suffice? Or maybe tickautomargin
? Or perhaps we're looking at this the wrong way, something like:
var layout = {
automargin: true || false,
automarginmode: 'all' || 'ticks' || 'title' || 'xaxis.title' || 'legend' || ...
// or any combination of these using '+',
// a 'flaglist' attribute like scatter 'mode'
}
might scale better.
@alexcjohnson @cldougl @chriddyp any thoughts on this?
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.
+1 for automargin
/ automarginmode
or at least (x|y)axis.automargin
- this is more clear if attributes other than ticks/tick labels contributes to this (now or in the future).
@etpinard which files should I look into for managing the dependencies among attributes for restyle/relayouts? I.e. the fact that my new option depends on |
Ready for re-review. |
@@ -47,6 +47,7 @@ var layoutOpts = { | |||
'*plot* calls `Plotly.plot` but without first clearing `gd.calcdata`.', | |||
'*legend* only redraws the legend.', | |||
'*ticks* only redraws axis ticks, labels, and gridlines.', | |||
'*margins* recomputes ticklabel automargins.', |
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 yes, that's a much better solution 🎉
_counteraxis: true, | ||
|
||
// don't use automargins routine for labels | ||
automargin: 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.
might be nice to let polar and ternary contribute to automargin in the future - but since this is an axis-level attribute I guess there's no problem ignoring that part for now.
Nice job @nicolaskruchten - nothing more from me, looks great! 💃 |
All right! Time to merge this thing 💃 |
@etpinard Hey Étienne. Can't thank you enough for all your work on plotly. Whats the timeframe for merging these fresh PR merges into the distributed plotly package. Curious if I should pull the repo and make my own local copy or wait for it to be in the distribution. |
This PR has been released in |
Gotcha, awesome. I just saw there was a new release yesterday, thanks for that. I'm puling my hair out trying to figure out why my automargin isn't working. Not sure if you can help me at all. both the legend + axis label collide with the axis labels. Here's my code for the layout:
The data is a little complex, but I'll try to make a codepen to emulate the exact plot I'm doing if you don't have any ideas initially. |
That's part of the known issues in #2243 (comment)
|
@yniknafs can you please create a new issue for this and I'll investigate? |
One thought on this is that the |
Aside from the legend overlap, this question about where the axis title goes has come up before - though I'm not aware of an issue discussing it. @yniknafs as you've deduced, we currently have the title avoid the bounding box of each tick label independently, so if there's a long label near the edge somewhere, that won't cause the title to shift outward (until you pan the axis to put that long label in conflict with the title, then it'll move). But you're right, in certain cases it would be nice to have a mode where the title stays out beyond the longest label even if they wouldn't actually cover each other. |
@alexcjohnson actually |
Ah I see, didn't realize those two were coupled but you're right, that does generally work and seems like a good standard behavior. I still think it would be nice to be able to decouple them, ie by default it works like you have it but you could use another attribute to force either separate-label avoidance with |
@nicolaskruchten @alexcjohnson |
Please add this to the 3D scene. It wont work in scatter3D. I'm getting the following error: `ValueError: Invalid property specified for object of type plotly.graph_objs.layout.scene.XAxis: 'automargin'
` because the automargins property belongs to "plotly.graph_objs.layout.XAxis:" and not "plotly.graph_objs.layout.scene.XAxis:" For my particular scatter3d plots, I need to use the scene. |
Proof of concept for addressing #296 and #1504.
The
Plots.autoMargin
andcalcBoundingBox
machinery is all already in place so I've wired them together such that margins can only grow, otherwise it's easier to trigger interactions between various axes such that the layout machinery loops without converging. Unfortunately this means that it's possible to get into situations where e.g. the left-hand margin is big enough to accommodate some long Y-axis labels which are then hidden because some long X-axis labels caused the plot to become shorter.Known issues with this PR:
Plots.doAutoMargin
to sum the requested_pushmargins
somehow. This is a known issue: Add legend 'auto' x|y and/or 'container' (x|y)ref #1199