-
-
Notifications
You must be signed in to change notification settings - Fork 387
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
Cursor scale value sync #216
Conversation
Hmm something funky is happening to the ranger selection period with this code. Selecting a range in the ranger always snaps to the end of the chart. I'll keep looking at it... |
Okay so now horz/vert scaling works as expected. Having both the vertical and horizontal ranger synced at the same time would require some more effort. I think that having vertical rangers is pretty niche (and having both is even nicher) though so I'd throw it on the back burner. Basically because the cursors are synced, it moves the horizontal ranger vertically when adjusting the vertical ranger and vice versa. So there would need to be some sort of logic somewhere to prevent that behavior on rangers (maybe an isRanger flag or something to that effect could help). |
thanks @EmiPhil i'm a bit busy tomorrow but i'll review this in the next day or two. in the meantime, please rebase, squash the commits and exclude also the Lines 238 to 244 in abacd02
|
f8e8b57
to
2aa0b9d
Compare
Should be ready for review. The bit inside of the cacheMouse function makes sense to me (perhaps there is a better way of calling The part I'm less confident about is the scaling sync. I haven't played with the code of the library enough yet to even know if putting logic in |
I'll loop back to this issue once we finish off #219 because getting the scaling to work will become the next issue otherwise |
agreed. i'd want to land that first since it's more straightforward. |
src/uPlot.js
Outdated
_x = plotWidCss * (_x/_w); | ||
_y = plotHgtCss * (_y/_h); | ||
// check for value based syncs in the src plot | ||
let [xKey, yKey] = src.cursor.sync.scales; |
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.
unless there's a reason why these config opts would differ between publisher (src) and subscriber (self), let's just pull this off of the syncOpts.scales
.
src/uPlot.js
Outdated
// check for value based syncs in the src plot | ||
let [xKey, yKey] = src.cursor.sync.scales; | ||
if (xKey != null) | ||
_x = self.valToPos(src.posToVal(_x, xKey), xKey); |
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.
we can definitely bypass self.valToPos()
since we're not gonna need canvas pixels, let's call the lower-level getXPos()
and getYPos()
directly. src.posToVal()
will probably stay (it's a bit cheaper).
src/uPlot.js
Outdated
scaleValueAtPos(select[LEFT], xScaleKey), | ||
scaleValueAtPos(select[LEFT] + select[WIDTH], xScaleKey), | ||
); | ||
if (drag.x && !(src != null && !src.cursor.drag[xScaleKey])) { |
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.
similar to comment above, unless src and self have different opts, we can just use the local drag[xScaleKey]
. also, not loving the double-negatives 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.
it looks like src != null
is serving the same purpose as e == null
at the top of mouseUp()
(to check if this is a synced event or a local event). in that case we should keep it uniform. i'd be okay switching to src != null
everywhere since it seems a bit clearer.
even better would be to alias to a better name at the top const isSyncReq = src != null
;
src/uPlot.js
Outdated
); | ||
// we are changing scale based on a sync request which may not | ||
// have the same scale that we do | ||
if (src != null && !src.cursor.drag[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.
local drag[k]
?
dist/uPlot.esm.d.ts
Outdated
@@ -115,6 +115,8 @@ declare class uPlot { | |||
declare namespace uPlot { | |||
export type AlignedData = readonly (number | null)[][]; | |||
|
|||
export type CursorScales = [string, string]; |
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.
please rename to SyncScales
left some comments. most related to the likelyhood of sync'd plots having different might have missed something, but that's round 1. also, please rebase. commit message should be "allow cursor & drag sync using scales instead of coords. close #215." |
Thanks, I should be able to get to this tomorrow morning. In terms of behaviour, right now what I was trying to achieve with this pull is that if you specify the scale keys it will use the values of the data at that point (maybe we can call this absolute position or something) and if they are null it uses the % method (maybe we can call this relative position?) Does that API make sense for you? |
Oh I should also mention that I think we may need to be able to sync the x and y separately. I don't want to get too into the details in just a comment from my phone right now so I'll write up something more comprehensive about what I mean later. |
i'm confused. we had a whole discussion about it in #215, i understand both the intent and API :)
are they not already synced separately? |
@EmiPhil let me know if you think we can wrap this up in the next couple days since i'd like to cut a new version. otherwise we'll push it to the next release cycle. thanks! |
I won't guarantee it! I just finished up some work stuff and will be working on this for the rest of the afternoon. As long as there aren't any surprises I hope to finish soon |
2aa0b9d
to
2277fe1
Compare
So this is much more cleaned up now. When I talk about "sync x and y separately" I'm thinking ahead on how to make it work with both rangers at the same time. It's really tough to put into words so I can try to throw together an example at some point. And there's definitely a possibility that what I'm thinking about will be trivial to do! But that is for tomorrow to look at. |
demos/sync-cursor-ranger.html
Outdated
if (vert) { | ||
let top = uRanger.valToPos(initYmax, 'y'); | ||
let height = uRanger.valToPos(initYmin, 'y') - top; | ||
let width = uRanger.root.querySelector(".over").getBoundingClientRect().width; |
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.
uRanger.bbox.width / devicePixelRatio
should give you the same (and more elegant)
demos/sync-cursor-ranger.html
Outdated
} else { | ||
let left = Math.round(uRanger.valToPos(initXmin, 'x')); | ||
let width = Math.round(uRanger.valToPos(initXmax, 'x')) - left; | ||
let height = uRanger.root.querySelector(".over").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.
can pull this from uRanger.bbox, too
src/uPlot.js
Outdated
@@ -1485,14 +1485,18 @@ export default function uPlot(opts, data, then) { | |||
return sc.min + pct * d; | |||
} | |||
|
|||
function yScaleValueAtPos(pos, scale) { | |||
return scaleValueAtPos(plotHgtCss - pos, scale); |
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.
since scaleValueAtPos()
already contains an internal check for xScaleKey
to auto-set the dim
. why not just move the automatic pos
inversion into there as well, instead of making a dedicated yScaleValueAtPos()
.
oh, i see. this might indeed be tricky, not to mention ultra-niche :D. i'm already somewhat hesitant to include the y-ranging demo for the same reason - i've never seen one or felt like i needed one. having the y ranger also makes the demo more complicated by adding an opts generator that uses i'd like to leave any further sync complication out of the core. even if it's not a lot more code, every additional nuance makes maintenance more difficult, and the returns here are way past diminishing here, IMO. |
Fair enough. If you don't want it in the core that makes this issue easier! If we take out the y axis example should we just put this example code into the ranger example that already exists? |
Oh one thing that may not be as niche is wanting an x ranger but still wanting to be able to zoom on the y axis on the main chart. I'll play with that to see how it behaves (that's going to be the behavior I'm looking for, I agree the y ranger is weird, though it does really help to make sure the code is working!) |
yeah i think that makes sense. i feel like the default behavior of sync should maybe change to |
src/uPlot.js
Outdated
|
||
if (select.show && (drag.x || drag.y)) | ||
if (select.show && (dragX || dragY)) |
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.
during mousedown, these will always match drag.x
and drag.y
, right?
if that's the case, we can leave this line unchanged and actually pull the dynamic dragX
and dragY
variables into the local else
scope (line 1649), since they will no longer be needed anywhere else.
yeah, for sure. |
6a8f367
to
b564b1e
Compare
Yep and moved |
actually, relying on only |
On branch 6a8 or b56? 6a8 is garbage I pushed it too eagerly |
i just pulled b56 and zoom variations has the mentioned issues. i don't see how we can really get away from relying on the dynamic/shared dragX and dragY because the autoscale behavior will depend on what actually happened rather than the drag.x and drag.y opts - those should just serve to manage the behavior during dragging. we can just reset dragX and dragY to the original opts in mouseup or something. |
Okay I got the clicking thing fixed, but I need some clarification for the expected behavior of the dygraphs one. We want it to act completely like the just x and y ones and so auto scale the other axis after zoom? |
well, there's not really a way to auto-scale x, but for y, yes. and not just the dygraphs one, but any time there is unidirectional dragging, which includes x-only and your omni/uni mixed-case variant as well (when below the uni threshold). |
Okay yeah. And I think the limitation on auto scaling has more to do with |
b564b1e
to
0b9b87a
Compare
This all seems to work well enough? Behavior for non sync charts behaves without limitations, but if we are syncing then we just use the drag.x and drag.y values to prevent some of the sync charts from auto scaling while others don't. |
0b9b87a
to
d6c7304
Compare
Most recent push brings back the setSelect function that should have never been nuked |
there's definitely some oddities that happen with syncing exactly to match the src selection during unidirectional dragging. in the sync-cursor demo, if you drag along x in the smaller chart that is auto-scaled to have a min of 10%, you'll see that 10% min reflected in the main chart and will frequently end up with an empty main chart after zooming - but you only dragged x! perhaps we do need to pub dragX and dragY to all subscribers so they know if they need to match an explicit x and/or y drag. also, |
d6c7304
to
6d0ee4a
Compare
For the timezone-dst I think that should be fixed by d6c7304. I accidentally deleted the setSelect hook cause I thought it was leftovers. For the second one, it is odd but myabe not wrong? Like if you think about it they are supposed to be synced together but at render they are out of sync. So when you zoom the x of the zoomed in chart you are effectively zooming x/y on the non synced charts. |
I guess my thought is that if you are explicitly syncing the x/y key of two charts, you would expect the two scales to reflect each other. I think in this sync cursor demo that isn't the intent - it should really only sync the x. (Especially considering the units aren't event the same across charts) |
And yeah taking out the "y" key and the uni/y drag in the cursor opts in that demo behaves more how you would want this type of chart to behave. (What does it even mean to sync y across different units? This is why we left the ability to have y be relative imo) |
6d0ee4a
to
12d0ddd
Compare
12d0ddd includes an updated cursor-sync demo to reflect the changes I just mentioned |
yes you're right, i didn't actually mean for my original branch to permanently include those sync-cursor changes [1] in the demo, it was more just playing around. i'll give this a final review later today. |
since |
👍 And the change in 6d0ee4a is to fix a visual issue where if you are only syncing one or the other axis it wasn't always showing the whole select on the other chart. |
12d0ddd
to
5f265d1
Compare
5f265d1 was a winner :) outstanding work! i will give you 20% of profits from uPlot. unfortunately, 20% of $0 is still $0 🤣 i'm gonna do some followups like merging the rangers into one demo and other minor stuff. |
Adds a scales array option to the cursor.sync of the signature [xKey, yKey] (default [null, null]) and includes a new demo showcasing functionality.
#215