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

Selection re-ordering for scatter traces with ids #1709

Merged
merged 5 commits into from
May 23, 2017

Conversation

etpinard
Copy link
Contributor

fixes #1706 (an attempt at least)

- this reorders the object-constant nodes (i.e. for traces w/ ids)
  correctly 🎉
- BUT, this also brakes the update pattern for traces with non-numeric
  (x,y), as the .remove call in Drawing.translatePoint throws the
  join selections out-of-sync, leading an exception!
@etpinard
Copy link
Contributor Author

FYI: more info on selection.order() here

@@ -483,6 +485,8 @@ function plotOne(gd, idx, plotinfo, cdscatter, cdscatterAll, element, transition
});
});

join.order();
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🆘 IMPORTANT 🆘

This commit breaks scatter/plot.js for traces with non-numeric (x,y) positions.

That is due to Drawing.translatePoint which is called on the enter and merge selections. When points with non-numeric (x,y) are detected, Drawing.translatePoint removes them. Unfortunately, this makes the join selections out-of-sync with the DOM and leads to 'TypeError: Cannot read property 'textContent' of null exception thrown by d3.


So, I have two questions for the d3-heads looking at this PR:

  • Is there a way to re-select the join and call order() on that selection?
  • Or should we make our updates more d3-esque by filtering data items with non-numeric position before binding them to the selections?

Copy link
Contributor

@n-riesco n-riesco May 22, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about join.filter(onlyNumeric).order()?

I'm not sure I understand how makePoints works.

Shouldn't these lines:

        enter.call(Drawing.pointStyle, trace)
            .call(Drawing.translatePoints, xa, ya, trace);

be:

        join.call(Drawing.pointStyle, trace)
            .call(Drawing.translatePoints, xa, ya, trace);

Otherwise DOM and data won't be in sync. Here's a codepen showing a similar issue.

... and order nodes on merge selection before
    styling/translating/removing-non-numeric item so that
    the order call acts on a merge selection in-sync with the DOM
@@ -419,9 +419,6 @@ function plotOne(gd, idx, plotinfo, cdscatter, cdscatterAll, element, transition
var enter = join.enter().append('path')
.classed('point', true);

enter.call(Drawing.pointStyle, trace)
Copy link
Contributor Author

@etpinard etpinard May 23, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@n-riesco thanks very much 🎉

Your solution appears to work:
peek 2017-05-23 11-43

My only concern now is how this interacts with animations. I think the reason why translatePoints and pointStyle are called on enter selection and merge selection has to do with this block below where entering nodes have their opacity transitioned from 0 to 1. I suspect this commit will break this behavior (although the tests are passing) has the entering aren't positioned and styled properly before opacity transition cc @rreusser . It would be nice to write a test case here 😏

I'm thinking instead, we could keep those enter.call(), make Drawing.translatePoints not (and maybe never) remove nodes and make the merge selection .each block below (which comes after the .order()) handle the cases where nodes need to be removed.

Thoughts?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the point you make about animations is important.
And the solution you propose (i.e. remove nodes only after we're done with the enter selection) is more robust.
👍 from me.

Copy link
Contributor

@n-riesco n-riesco May 23, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@etpinard after thinking a little bit about it. I'm still think that translatePoints and pointStyle should be applied to the merge selection to keep in sync DOM and trace.


I haven't tested it, but .order should take care of my concern.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm still think that translatePoints and pointStyle should be applied to the merge

That's always been the case here and here.

- handle that in scatter/plot.js
- call Drawing.pointStyle and Drawing.translatePoints on enter
  selection to ensure that added point are drawn in
  with a smooth transition
@@ -539,6 +539,42 @@ describe('end-to-end scatter tests', function() {
.catch(fail)
.then(done);
});

it('should smoothly add/remove nodes tags with *ids* during animations', function(done) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test is failing after c8c3197

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and fixed in commit 9c35675

Before 9c35675
peek 2017-05-23 14-09

After:

peek 2017-05-23 14-08

@@ -56,7 +56,7 @@ drawing.setRect = function(s, x, y, w, h) {
*
* @return {boolean} :
* true if selection got translated
* false if selection got removed
* false if selection could not get translated
*/
drawing.translatePoint = function(d, sel, xa, ya) {
Copy link
Contributor Author

@etpinard etpinard May 23, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To my surprise, Drawing.translatePoint is called only in scatter/plot.js and box/plot.js:

image

Note that this patch does not affect the box/plot.js logic where points with non-numeric positions are filtered out of the data bind.

@etpinard etpinard added this to the 1.28.0 milestone May 23, 2017
@etpinard
Copy link
Contributor Author

Tagging this as reviewable, pinging @alexcjohnson (as this is a pretty important PR) and putting this under the 1.28.0 milestone (as the changes here are too heavy for a patch release IMHO).

Thanks again @n-riesco for your help!

@@ -430,6 +427,8 @@ function plotOne(gd, idx, plotinfo, cdscatter, cdscatterAll, element, transition
var markerScale = showMarkers && Drawing.tryColorscale(trace.marker, '');
var lineScale = showMarkers && Drawing.tryColorscale(trace.marker, 'line');

join.order();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we need join.order() twice?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

one for marker nodes, one for text nodes

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah right - didn't notice join got reassigned between 👍

@alexcjohnson
Copy link
Collaborator

Looks great, nice work! 💃

@etpinard etpinard merged commit 13c1237 into master May 23, 2017
@etpinard etpinard deleted the scatter-id-selection-order branch May 23, 2017 19:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Data ids interfere with transforms
3 participants