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

Auto X / Y Zoom #219

Closed
EmiPhil opened this issue May 14, 2020 · 21 comments
Closed

Auto X / Y Zoom #219

EmiPhil opened this issue May 14, 2020 · 21 comments

Comments

@EmiPhil
Copy link
Contributor

EmiPhil commented May 14, 2020

Reference drawing:

image

Say you are trying to zoom from the point Mo.

What I'd like to do:

  1. If the cursor is in the invisible green band of height x pixels, zoom in on the X axis.
  2. If the cursor is in the invisible purple band of width x pixels, zoom in on the Y axis.
  3. If the cursor is in the invisible burgundy intersection of height/width x pixels, don't zoom at all
  4. If the cursor is in any of the four areas outside of the bands, zoom in on both the X and Y axis.

Hopefully that explanation makes sense!

Is this something that should be done via a plugin or would you consider it being part of the lib?

Thanks.

@EmiPhil
Copy link
Contributor Author

EmiPhil commented May 14, 2020

A while ago I threw together a quick proof of concept while evaluating AM charts, so this kind of shows what I mean interactively (but doesn't work super great).

https://codepen.io/Akaritzu/pen/RwPGREx

@leeoniya
Copy link
Owner

leeoniya commented May 14, 2020

dygraphs has something like this too (try on the home page): http://dygraphs.com/

except it only flips between either x or y but never both. from the amcharts codepen, i think having all 3 variants causes too much UI flashing during the selection...and you dont really save that much time over just doing an x zoom followed by a y zoom.

i might be open to having the dygraphs style if it doesn't add too much code or option complexity. i don't think we need to go as far as adding thresholds but just use whichever is greater of dx or dy to toggle between x or y scale. that means another option, e.g. cursor.drag: {x: true, y: true, bidi: false}

bidi might be too writing/text-centric a term. omni: false could work instead.

that being said, if what you need is the 3-variant flavor, having the 2-variant in the core won't help you much - you'll probably need to make a plugin that implements all the selection logic.

@EmiPhil EmiPhil mentioned this issue May 14, 2020
@EmiPhil
Copy link
Contributor Author

EmiPhil commented May 14, 2020

So doing it both ways is fairly straightforward if it works for you.

Basically instead of setting omni to a boolean I used a number:

omni = 0 means use the default behavior
omni = 1 means use omni with no threshold (the dygraph way)
omni > 1 means use omni with a threshold of omni (the way I'm looking for)

@leeoniya
Copy link
Owner

not bad, actually.

except that the PR breaks unidirectional zooming 🤣

@EmiPhil
Copy link
Contributor Author

EmiPhil commented May 14, 2020

🤦

Maybe the next thing to look at is #72

@leeoniya
Copy link
Owner

that's a one large can of worms, my friend. even #175 wouldn't have saved you in this case.

@EmiPhil
Copy link
Contributor Author

EmiPhil commented May 14, 2020

Okay new push should solve it. I was only test uni on the non omni charts! Relatively simple fix this go around.

@leeoniya
Copy link
Owner

leeoniya commented May 14, 2020

if we're gonna do the threshold thing, the API has to change.

x: true, y: true already implies omnidirectional dragging. if i additionally set omni: 0, i expect it to restrict that assumption somewhat, but it actually does nothing, and omni: 1 acts as the restriction, which then is further restricted with omni: 1+. that's an ultra-confusing API.

we'll need 2 explicit opts. omni should be true/false (with a default set to whatever x/y opts imply unless explicitly set, e.g. drag.x && drag.y). then the threshold should be an additional opt. i use cursor.focus.prox in a similar capacity to focus series based on minimum cursor proximity.

or something else. i havent thought this through too hard. suggestions welcome.

@EmiPhil
Copy link
Contributor Author

EmiPhil commented May 14, 2020

I guess the hard part is differentiating between omni as in the x/y window (how it is today) and omni as in the dygraph example. I took "omni" to mean "dygraph" style (so that x: true, y: true does not imply omni). Edit: Though I think omni: true does imply x and y are true!

Maybe omni is the wrong terminology?

Regardless, setting two params works for me so I can get that part done.

@EmiPhil
Copy link
Contributor Author

EmiPhil commented May 15, 2020

Okay, that's done.

omni is a boolean, and omniThreshold is a number that defaults to -1 (dygraph style). If omniThreshold is positive, it is used as the threshold.

Doing it this way was kind of interesting, because you could say that a threshold of 0 would be equivalent to the window style (how it is today). I'm starting to think that the dygraph style is the edge case

So if you want we could dump the omni: true option completely, set threshold default to 0, if drag.x && drag.y we use the omni code. Setting threshold to a positive integer gives the behavior I was originally after, setting it negative gives dygraph?

@leeoniya
Copy link
Owner

leeoniya commented May 15, 2020

the original intent is that drag.x and drag.y unlock dragability on those axes and i'd like to maintain these semantics. switching from omni to uni could be the path forward. effectively when both x/y are true (omni) we bring back limited unidirectional behavior via an explicit uni upper threshold. so the variants become:

x only (unidirectional, default):

x: true
y: false
uni: null

y only (unidirectional):

x: false
y: true
uni: null

x & y (omnidirectional):

x: true
y: true
uni: null

dygraphs (unidirectional up to 1e9, effectively Infinity):

x: true
y: true
uni: 1e9

your needed case (unidirectional up to 35, otherwise omnidirectional):

x: true
y: true
uni: 35

the last two options now share the same uni testing logic. when x: true, y: true, all unidirectional behavior will use the larger of dx or dy to determine the direction.

this feels pretty good to me; i can explain it without too many mental gymnastics.

thoughts?

@EmiPhil
Copy link
Contributor Author

EmiPhil commented May 15, 2020

Yep that makes sense to me

@EmiPhil
Copy link
Contributor Author

EmiPhil commented May 15, 2020

If uni is set should we infer that x and y must be true?

@leeoniya
Copy link
Owner

yeah, should make the code cleaner.

@leeoniya
Copy link
Owner

also, let's include all the above variants in a single demo. in least to most complex order.

@EmiPhil
Copy link
Contributor Author

EmiPhil commented May 15, 2020

Done.

The way I got it to work was by saying that if we are in the burgundy square then use the dygraph way. In my original use case I would have made that zoom omni, but when you have a small enough threshold I think it should work for my users.

Since a uni of infinity means that the whole chart is basically the burgundy square, all works as expected.

The variations also show that setting uni to 0 == omni which I think is logical. But the value is set to null instead of default 0 so that we can skip computations if the user didn't specify uni.

@leeoniya
Copy link
Owner

leeoniya commented May 15, 2020

nice. seems to work well.

final tweaks:

  • move the dygraphs-style demo after the two unidirectional demos and drop the uni: 0 demo (i agree it's useful as a test case, but it does nothing to show off a unique zooming variant). the demo progression should be: x-only, y-only, x or y (adaptive), x & y (omni), adaptive + omni
  • the graphs can be vertically smaller by 50%, to fit more on the screen.
  • simplify the commit message to just "adaptive unidirectional drag-zoom. close Auto X / Y Zoom #219."

EmiPhil pushed a commit to EmiPhil/uPlot that referenced this issue May 15, 2020
@EmiPhil
Copy link
Contributor Author

EmiPhil commented May 15, 2020

Done!

@leeoniya
Copy link
Owner

awesome!

https://leeoniya.github.io/uPlot/demos/zoom-variations.html

thanks for doing the good work and sticking through my nagging :)

i'm gonna do a couple follow-ups to add it to the demos index and add comments to the typings file about how uni works.

@leeoniya
Copy link
Owner

did some minor refactoring tweaks here:

188e521#diff-0e94ed30087b62b2ab95466722c48f84

i swapped dx/dy distance testing to give dragX precedence when dx == dy.

otherwise everything should still be logically equivalent 🤞.

@leeoniya
Copy link
Owner

leeoniya commented May 15, 2020

i found a small bug (fixed by 0895041)

when the uni-directional zooming is done along x, the y failed to autoscale within the zoomed x range because the mouseup handler only inspected the drag.x/drag.y opts rather than the new dragX & dragY vars.

i hoisted the vars up into a cache, next to dragging.

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

No branches or pull requests

2 participants