-
-
Notifications
You must be signed in to change notification settings - Fork 1.9k
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
Cross-chart context management #1989
Conversation
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.
Thanks very much @dfcreative for taking this on!
You're up to a solid start.
|
||
// create canvases only in case if there is at least one regl component | ||
// FIXME: probably there is a better d3 way of doing so | ||
for(var i = 0; i < fullLayout._modules.length; 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.
using fullLayout._has
would clean this up.
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.
fullLayout._has
works for plots, not traces, if I get it right
fullLayout._glcanvas = fullLayout._glcontainer.enter().append('div') | ||
.classed('gl-container', true) | ||
.selectAll('.gl-canvas') | ||
.data([{ |
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.
No need to make objects here: .data(['context', 'focus', 'pick'], String)
would suffice, where String
is the key function.
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.
@etpinard these objects are used by parcoords later, strings do not allow for putting additional stuff in them.
In particular, https://github.com/plotly/plotly.js/pull/1989/files#diff-2fad23de82bf8c7625ea8e11436233b7R301
for(var i = 0; i < fullLayout._modules.length; i++) { | ||
var module = fullLayout._modules[i]; | ||
if(module.categories && module.categories.indexOf('gl') >= 0) { | ||
fullLayout._glcanvas.enter().append('canvas') |
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.
Ok. so this create the canvases properly, but we'll need to clear them when fullLayout._has('gl') === false
.
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 is a bit more complicated, from interaction tests... Working on that. Sometimes we deal with
Plotly.plot(document.body, [{}]);
Plotly.restyle(document.body, {type: 'parcoords', x: [[1]]}, 0);
extendDeep( | ||
{}, |
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.
Why did you change this?
colorAttributes
and friends are used in other place in plotly.js, so we can't just extend them here. We need to make a copy of them and then overwrite a few keys.
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.
@etpinard colorAttributes(str)
returns a new object every call, no need to copy a copy.
@@ -28,8 +27,6 @@ exports.clean = function(newFullData, newFullLayout, oldFullData, oldFullLayout) | |||
var hasParcoords = (newFullLayout._has && newFullLayout._has('parcoords')); | |||
|
|||
if(hadParcoords && !hasParcoords) { | |||
oldFullLayout._paperdiv.selectAll('.parcoords-line-layers').remove(); | |||
oldFullLayout._paperdiv.selectAll('.parcoords-line-layers').remove(); |
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.
Where is this done now?
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 we keep canvases, I guessed we have to store them instead of recreating every time.
Actually removing and creating anew would solve the problem... Should I try to do that?
x: 0, | ||
y: 0, | ||
width: canvas.width, | ||
height: canvas.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.
Very nice simplification 👌
@@ -248,6 +261,13 @@ module.exports = function(canvasGL, lines, canvasWidth, canvasHeight, initialDim | |||
} | |||
}, | |||
|
|||
viewport: { |
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.
Lovely ❤️
@@ -253,8 +244,7 @@ function styleExtentTexts(selection) { | |||
.style('user-select', 'none'); | |||
} | |||
|
|||
module.exports = function(root, svg, styledData, layout, callbacks) { | |||
|
|||
module.exports = function(root, svg, parcoordsLineLayers, styledData, layout, callbacks) { |
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.
cc @monfera
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.
Adding parcoordsLineLayers
to this renderer function is fine, because their creation is no longer subordinated to this, the layers need to come from the outside so they're also usable for scatter plot etc.
@@ -13,8 +13,9 @@ var parcoords = require('./parcoords'); | |||
module.exports = function plot(gd, cdparcoords) { | |||
|
|||
var fullLayout = gd._fullLayout; | |||
var svg = fullLayout._paper; | |||
var svg = fullLayout._toppaper; |
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.
Why?
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.
For proper z-index.
.gl-container
resides between two svg layers, unlike .parcoord-layers
container, which was always behind svg layers.
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.
Right, nice. So the parcoord axes are now drawn in _toppaper
, correct?
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.
@etpinard yep, I tried to test FPS - seems to work fine
Looks great! Performance seems to be identical too. There's some slight increase to what gets cleared on the right hand side, compare: with new: No big deal but it'd be nice to just repaint (clear) just the impacted area.
|
@monfera I am facing an issue here when we have multiple parcoord traces put one over the other. Discussing that with @etpinard that seems to be an important feature and we can't just drop it with this PR. I don't see easy fixes for that but creating a separate What are your thoughts on that? |
@dfcreative superimposing So it feels there's no use case for overlaying multiple parcoords without adding a bunch of glue functionality at least. See next comment for more. |
From dy/plotly-contrib#14 multiple overlaying parcoords instances: how do we workaround that?As mentioned above, the need is probably extremely rare or nonexistent, especially given that the overlaid parcoords would still render and interact independently. But the question is generally applicable, for example, for scatter plots. While it's not the immediate step, the very idea of sharing canvases is to remain below the 16 WebGL context limit, which assumes that the same canvas may have a bunch of scatterplots on them, and overlaying would have a use there too. I'm thinking of the following possibilities, please let's extend it if there are other solutions:
I'd check if current Barring this, option 3 looks reasonable. A global dictionary would keep track of overlapping panels or plots and redraw in the rare case there's overlap. Whatever the solution, it'd be great to abstract out the notion of the A tricky question is the pick layer: the colors (RGBA encoding) should become global in that both the layer (plot) and the point (data point represented with a marker or line) can be recovered. A simpler option is if, as per painter's algo, only the top layer in an overlapping stack would capture mouse events. clearing canvases: what's strategy?Current gl3d plots: should we introduce shared canvas there?The motive for sharing canvas is to not run out of WebGL contexts. It's less likely that 3D widgets are numerous, eg. a grid of various surfaces, than having SPLOM or 2D small multiples, but it can happen, so it's a prioritization question @etpinard ? Also, I think overlaying multiple 3D widgets, or 2D + 3D widgets would be very rare or useless, so maybe we could follow option 1 for these. In other words, if there's a need to support lots of independent 3D widgets, then they could be rendered onto the same set of canvas (may or may not be the same set as the 2D canvases), as long as overlaps are prohibited or unsupported (imo). |
@monfera I see your point from a developer's perspective. It is true that superimposing parcoords traces isn't a typical nor a useful use case. But from a user's perspective, superimposed subplots is part of the plotly.js API, it should just work. Moreover from a maintainer's perspective, superimposing parcoords does work currently, so we can't just break that. So, I think we're left with two options:
|
@etpinard I totally see your point ie. the value of the standard and pragmatism, and it was the dataviz perspective rather than the developer perspective from which overlapping parcoords didn't look particularly useful to me. In other words, there are hundreds of other parcoords enhancements that would be more useful than enabling overlaps (again, from a dataviz pov). I'll check with our users though to be sure. The standard of being able to overplot anything with anything may have come from 1) it's a given with SVG; 2) it's also a given with WebGL as long as contexts are kept separate. This ticket is partly about undoing 2) to let a user have a few parcoords and/or other WebGL things. I should have no vote in which way it falls because of having no resource / product management or architect role but glad to chime in: Siding with your option 2 (rule out overlapping) has the benefit that it retains freedom of movement, ie. we can postpone the questionable functionality of parcoords overlap until a customer strongly needs it. If that happens, perhaps it won't just be a 'dumb' overplot anyway. By contrast, going with (retaining) option 1 just binds us to a functionality that has tech drawbacks, e.g.
for little to no offsetting benefit. |
^^ I think the biggies are the abilities to share
|
We have client feedback confirming that there's no need for overlapping parallel coordinates. A subsequent future option (maybe layering related, maybe not - served to a good extent by currently supported functions) is under clarification. |
It looks like the function in question wouldn't be achieved (or achievable) by distinct overlapping |
Closing. Superseded #2159 |
Replaces #1986.
Following regl-summer meeting.
Makes parcoords use shared canvases and enables viewport/scissor instead of padding canvas.
There is single image diff
gl2d_parcoord_2
.That seems to be due to pixel snapping by browser padding/margin, whereas
viewport
keeps precise values.All other tests pass.