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

dynamic gutter sizing #362

Closed
torkelo opened this issue Nov 8, 2020 · 10 comments
Closed

dynamic gutter sizing #362

torkelo opened this issue Nov 8, 2020 · 10 comments

Comments

@torkelo
Copy link

torkelo commented Nov 8, 2020

uPlot places quite long x-axis ticks close to the right edge so the x-axis labels are cut as they are rendered outside the confines of the graph.

Not sure of a way currently to fix this from the outside, I cannot change the right-side gutter only, and even if I could I would not know where the labels are placed from the outside (I think) so would not know what to change it to.

Screen Shot 2020-11-08 at 09 16 30

@leeoniya
Copy link
Owner

leeoniya commented Nov 8, 2020

Not sure of a way currently to fix this from the outside,

i'm not exactly sure how to fix it from the inside either :D.

I cannot change the right-side gutter only

i'm pretty sure that the gutter is only added to the side without any cross-axes whose tick labels may already serve as a buffer. so this setting would not apply to both sides unconditionally.

i could look into doing something similar to axis.size that allows for dynamic gutter adjustment but this runs the risk of introducing more redraws than necessary since adjusting available total space for ticks also changes their spacing and the ticks themselves.

i'm on a phone an can't check right now exactly how other libs behave but when your labels can be fully dynamic based on zoom, i don't see an easy way to handle this.

flot seems to have fixed gutters, too?

https://www.flotcharts.org/flot/examples/axes-tickLabels/index.html

in any case, i think 0-sized gutters are a non-starter except for sparklines or non-zoomable charts where the label spacing can be predicted or controlled.

do you have any suggestions?

@leeoniya
Copy link
Owner

leeoniya commented Nov 8, 2020

and even if I could I would not know where the labels are placed from the outside (I think) so would not know what to change it to.

actually this is possible with 1.3. the axis splits are exposed as axis._splits and values are axis._values.

probably the route here would be to offer the same for gutters as with axis.size and allow for dynamic sizing so you can do measureText as you feel necessary.

@torkelo
Copy link
Author

torkelo commented Nov 8, 2020

what is splits? And how would Grafana know how far to the edge the tick value is placed to know how to calculate the right side gutter

@leeoniya
Copy link
Owner

leeoniya commented Nov 8, 2020

splits are the raw scale values of the ticks (and gridlines). you can get their coords through the u.valToPos(value, scaleKey) api, then compare that to the plotting area's bounding box (u.bbox). combined with measureText of the corresponding formatted axis._values[i] would get you to a possible decision.

@leeoniya leeoniya added the question Further information is requested label Nov 11, 2020
@leeoniya leeoniya added enhancement and removed question Further information is requested labels Nov 14, 2020
@leeoniya leeoniya changed the title Axis: X-Axis labels rendered outside chart container dynamic gutter sizing Nov 14, 2020
@leeoniya
Copy link
Owner

@torkelo

i'm working through this now. however, this plus dynamic axis sizing requires me to switch uPlot's whole rendering strategy from sync to async (via queueMicrotask, or maybe falling back to Promise.resolve().then()) to support efficiently converging geometries as part of pretty much all core APIs. the existing sync render strategy is becoming untenable under non-static geometry (see #359).

progress so far is encouraging, but hook firing order still needs to be carefully reviewed to provide as many previous guarantees as possible. a few demos are still broken, due to this i think.

@torkelo
Copy link
Author

torkelo commented Nov 17, 2020

Thanks! that's great.

Would love to know more about the rendering flow., I would assume axis ticks are generated first, then axis space & size are measured, then what's left i for the graph.

@leeoniya
Copy link
Owner

long story short, it's part of splitting "compute" from "draw" so the two cannot re-trigger or double-call each other in unpredictable ways. e.g. right now the some axis size changes are detected at the top of the draw pass, and a draw is re-triggered with a bail-out. this is ultra tricky to comprehend and keep track of. by splitting out the change detection, having it set flags and and deferring the draw pass, i get call batching and predictability for free. even the current public batch method goes through some hairy flag twiddling to end up in the right place (since some functions it calls can themselves trigger draw, or scale updates, or cursor sync, etc.).

e.g. search for _queuedResize bits in https://github.com/leeoniya/uPlot/blob/ad08439d701c775df4533be3a60604e2e0307a8a/src/uPlot.js.

or the public u.batch() logic:

uPlot/src/uPlot.js

Lines 1784 to 1799 in ad08439

let inBatch = false;
let shouldPaint = false;
let shouldSetScales = false;
let shouldUpdateCursor = false;
// defers calling expensive functions
function batch(fn) {
shouldSetScales = shouldUpdateCursor = shouldPaint = didPaint = false;
inBatch = true;
fn(self);
inBatch = false;
shouldSetScales && setScales();
FEAT_CURSOR && shouldUpdateCursor && updateCursor();
shouldPaint && !didPaint && paint();
shouldSetScales = shouldUpdateCursor = shouldPaint = didPaint = inBatch;
}

@leeoniya
Copy link
Owner

leeoniya commented Nov 17, 2020

sorry, the above answered your Slack question only.

Would love to know more about the rendering flow., I would assume axis ticks are generated first, then axis space & size are measured, then what's left i for the graph.

it goes scales -> splits (based on scales, incrs, space) -> values -> axis.size (re-eval and if changed, resize() and loop back to splits until the re-eval converges to no changes). once this is done it draws grid and ticks and values at the splits offsets.

@leeoniya
Copy link
Owner

the core of this is now done: https://leeoniya.github.io/uPlot/demos/axis-autosize.html

@torkelo
Copy link
Author

torkelo commented Nov 18, 2020

Nice! Might have found a bug if I zoom in on that demo it hangs the browser

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants