-
Notifications
You must be signed in to change notification settings - Fork 1.8k
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
Initial version of zoom with _zoomOutRestrict using D3 zoom features #1385
Conversation
I have completed work that I had targeted for this iteration. Will wait for your feedback. I believe that if we carefully check what we debounce and switch off transitions during zoom/brush, we can do updates online. Once this PR is accepted, I will do a PoC - based on the results we may decide to merge that one. If that works it will give a much better user experience. For now I will move onto some other task. |
Wow, this is a valiant effort. I can see that it already works better than the old version, where it was possible to keep panning off the edge. (It wouldn't display out of bounds, but you'd have to pan all the way back for panning to start working again.) It's a pity about all those "don't fire event" flags. I am familiar with the infinite recursion of events as it happens in a lot of UI. Sometimes there is a good way to refactor, sometimes no. I am going to look closely at this before merging. |
Currently I have introduced a second argument to the |
src/coordinate-grid-mixin.js
Outdated
// Our zooming is not standard d3 zoom as defined in their examples. | ||
// Instead we want to focus the chart based on current zoom transform. | ||
// The following code computes values of new domain the same way as transform.rescaleX. | ||
// The difference is what we do after we get the newDomain |
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 this is the same as rescaleX
, why not just use rescaleX
?
I'm diving into this today. Feels like we're fighting with |
src/coordinate-grid-mixin.js
Outdated
d3.event = null; | ||
if (!event.sourceEvent) { return; } | ||
|
||
var newDomain = _zoomTransformToDomain(event.transform, _origX); |
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 seems to work afaict:
var newDomain = event.transform.rescaleX(_origX).domain();
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 think it would. :) The more I am getting to know D3 the more I admire.
I reached this code independently step at a time (using drawings on a paper with pencil). Just now rechecked - this code seems to do exactly the same. Let me know if you would change or should I.
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.
Okay, great. I am working on it.
I hope to also clean up the recursive event stuff and merge this today. (The zoomHandler
argument makes sense to me, but setting d3.event
seems messy so I'm looking at using the same strategy there.)
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 d3.event
, I could not understand their reasoning behind keeping the event in a global variable. I think it would have been much cleaner design for D3 authors as well as users if D3 passed the event as parameter to event handler (or, it already does and I am unaware).
I had started setting it to null
as I was getting occasional infinite loop. Maybe those were caused by something else. D3 examples don't do that.
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 don't understand the global either, and I don't remember seeing a rationale for it. It is what it is. 😉
Yes, it's easy to repro the infinite recursion. I'm experimenting with using arguments similar to noRaiseEvents
to fix this. Still makes me think that we might have things inside-out, at least for the new way that d3.zoom
works.
However, it's likely that the problem is abstraction itself: often things that are easy in ad-hoc UI code become really complicated when you try to make the code reusable. A lot of people reject dc.js for this reason, but I still think it's worth it!
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.
Actually I am not sure about others, if it was not for dc, I would not dare to build the functionality that I need using D3 directly. D3 is like an ocean for a new person.
So definitely worth it.
use function declarations & do not underscore if it won't be reassigned cleanup for #1385
Made good progress last night on the recursion and general cleanup - optimistic that I'll finally merge today. That's really cool when you work out something with pencil and paper, and it turns out you've rediscovered something in the library. I bet that It's great how much special-purpose code you were able to eliminate here, because |
The zoom and brushing were the biggest changes I undertook. Great to see that it is reaching closure. 👍 I understand the code that you mean in |
Yes, this is a huge change and well done. Just the event recursion needs to be fixed. I'm closer but still haven't quite figured out the right strategy. As for bar charts, I don't think anything was copied from d3; I think it's an independent implementation. I misspoke, ordinal-scale charts do use Potential problem: bars can be of varying width, see e.g. a bar chart aggregated by month. I don't know if d3v4 has a new way to do that. I've never thought it was a great idea, but people apparently rely on it. It's natural to want to use a d3 time scale for the X axis, so that precludes rangeBands or scaleBand. |
Hi @kum-deepak, I fixed the recursion problem by using a reference count - whenever we're in a section which we think should not generate events, we increment a counter, and the event is not processed if the counter is greater than zero. This is commit 3f16842 on the It's not all that elegant but it says exactly what it means and it doesn't modify I know you are occupied with other things, but in case you have a couple minutes, could you take a look at the failing test cases on this branch? (build log) The first two may not matter, since we are just applying zoom extent later:
But these two seem to suggest that
If you have any thoughts, please let me know. Otherwise I will try to fix them tomorrow (and finally merge this!) |
I will have some time later in the day today (your time tomorrow morning) I will have a look. 👍 |
Hi @gordonwoodhull, went through your commit. I find that you are trying to restrict more than it should. I am not quite happy with the solution. If it is ok with you, I will make one more attempt. In my previous analysis I had found that I needed to distinguish a (d3) event generated - in response to a user action or a programmatic call. When a d3.event is generated and event handler is called in response to a programmatic calls - we do not need to execute the event handlers (the return from the top of the function). In this attempt I will go through the D3 event documentation in detail - may be skim their code. Go through their sample code again. In my last attempt I was trying to solve many moving things, in this one it is only one. I am quite hopeful to find a solution without making an unacceptable call onto d3.event. Will you be willing to wait for the weekend on this one? |
Sure, I'm open to other solutions - it was mostly the setting of Keep in mind that your original PR has the same exact errors - so I don't think I changed the behavior. I think it's the same idea - the handler sets something so that if recursion hits the same spot deeper in the call stack, it bails out. My technique was simply, we are the caller of d3 so we know we want to ignore any events generated deeper in the same call stack. No rush. |
…/range-series.html
Now I have understood how d3 is setting up event. When setting up an event if there is already a So, from within an event handler, if any call would directly or indirectly generate an event, it will set the current event as sourceEvent. I have gone through their examples, each example uses their specific conditions (e.g., https://bl.ocks.org/mbostock/6232537 and https://bl.ocks.org/mbostock/6232620) to detect programmatic event vs user initiated. Generally for brushing, the sourceEvent will be one of the mouse or touch events when our handler first gets called. The recursive call will have of the d3 generated events as source event. This is my basis for check to terminate recursion. With that understanding updated specs - simulated brush events - now clears d3.event when the handler finishes. I have committed changes for brushing, next target for zoom. :) |
Thanks @kum-deepak! This is great - it solves the recursion problem without resorting to setting One small ask: would you please re-apply the function declaration changes I made? To me var _f = function(...) { ... }; means function f(...) { ... } means grep 'var.*= function' src/* I see that our codebase is only about 90% consistent with my understanding, but I don't know how to write a jshint/jscs rule for this. I know that many JS developers prefer to use use Also, the same tests are still failing, but maybe the zoom fixes will help with that. |
Also, you dropped the commit messages on the fixes of mine you took in "Minor cleanup" |
Reapplied the function declaration changes. There are about 10 such cases in base and core. Outside of these around 20 cases. Create a task if I should make an attempt to convert all these. |
Sorry about the commit message, my code had gotten thoroughly messed up, so started from 3.0 and started applying code in different order than original commits (by copying and pasting). So, actually final commit-set does not have all the commits, while code is nearly the same. If you find anything else missing, just let me know. |
OK great, glad we're on the same page. I'm still on my walk but I'm looking forward to continuing my review when I get back. Looks like this is almost ready to merge, too, except for those zoom out tests. Good work! |
Pushed last commit for today :) |
How is your health, hope much better now 👍 |
Done with current set of changes :) Let me know if any blockers are there for merge. |
Learnt another hard lesson (again) about global variables :) If a test case involving simulated brushing failed, some other test may mysteriously fail. Later figured out it was happening because I think we should raise a ticket in D3 requesting them to (gradually) remove |
Looks like the rationale is consistent callback signatures? d3/d3#1887 |
Merged! Thanks @kum-deepak! |
I realized that 2 minor commits (the recent ones) did not get merged. I will create a small PR with those. :) |
I just saw that - already fixed. I had merged earlier version of branch by accident! |
And thanks for asking after my health. I am doing very well but I still have one arm in a sling. Looking forward to being able to touch-type in a couple weeks! |
This is work in progress. Please check using http://localhost:8888/web/zoom/restrict-panning.html
I need suggestions at this stage:
The above gives slightly peculiar UI glitches because some updates get applied immediately while some are debounced.
Quite a lot of code has changed, I will refactor before making final commit :)