From 8fe669b5e10ab3661a22586e35cc3d180e4e53cd Mon Sep 17 00:00:00 2001 From: Dmitry Date: Tue, 21 Mar 2017 14:52:16 -0400 Subject: [PATCH 01/61] Update gl3d camera --- src/plots/gl3d/camera.js | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/src/plots/gl3d/camera.js b/src/plots/gl3d/camera.js index e97fc8b213a..93d92e5ba08 100644 --- a/src/plots/gl3d/camera.js +++ b/src/plots/gl3d/camera.js @@ -14,6 +14,7 @@ var now = require('right-now'); var createView = require('3d-view'); var mouseChange = require('mouse-change'); var mouseWheel = require('mouse-wheel'); +var mouseOffset = require('mouse-event-offset'); function createCamera(element, options) { element = element || document.body; @@ -179,8 +180,24 @@ function createCamera(element, options) { return false; }); - var lastX = 0, lastY = 0; - mouseChange(element, function(buttons, x, y, mods) { + var lastX = 0, lastY = 0, lastMods = {shift: false, control: false, alt: false, meta: false}; + mouseChange(element, handleInteraction); + + // enable simple touch interactions + element.addEventListener('touchstart', function(ev) { + var xy = mouseOffset(ev.changedTouches[0], element); + handleInteraction(0, xy[0], xy[1], lastMods); + handleInteraction(1, xy[0], xy[1], lastMods); + }); + element.addEventListener('touchmove', function(ev) { + var xy = mouseOffset(ev.changedTouches[0], element); + handleInteraction(1, xy[0], xy[1], lastMods); + }); + element.addEventListener('touchend', function() { + handleInteraction(0, lastX, lastY, lastMods); + }); + + function handleInteraction(buttons, x, y, mods) { var keyBindingMode = camera.keyBindingMode; if(keyBindingMode === false) return; @@ -225,9 +242,10 @@ function createCamera(element, options) { lastX = x; lastY = y; + lastMods = mods; return true; - }); + } mouseWheel(element, function(dx, dy) { if(camera.keyBindingMode === false) return; From c1a4c0c487f4a3b845be6e0003032be040940327 Mon Sep 17 00:00:00 2001 From: Dmitry Date: Tue, 21 Mar 2017 14:58:54 -0400 Subject: [PATCH 02/61] Add dep --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 79d1d24551d..2d9a5458168 100644 --- a/package.json +++ b/package.json @@ -83,6 +83,7 @@ "gl-surface3d": "^1.3.0", "mapbox-gl": "^0.22.0", "mouse-change": "^1.4.0", + "mouse-event-offset": "^3.0.2", "mouse-wheel": "^1.0.2", "ndarray": "^1.0.18", "ndarray-fill": "^1.0.2", From 97b827e76734bcb84d7bc3546ac6e84bb5ff32d5 Mon Sep 17 00:00:00 2001 From: Dmitry Date: Tue, 18 Apr 2017 11:46:14 -0400 Subject: [PATCH 03/61] Add scattergl box/lasso icons --- src/components/modebar/manage.js | 5 +-- src/traces/scattergl/index.js | 1 + src/traces/scattergl/select.js | 73 ++++++++++++++++++++++++++++++++ 3 files changed, 76 insertions(+), 3 deletions(-) create mode 100644 src/traces/scattergl/select.js diff --git a/src/components/modebar/manage.js b/src/components/modebar/manage.js index f3732850f8c..c34f3deb73a 100644 --- a/src/components/modebar/manage.js +++ b/src/components/modebar/manage.js @@ -72,7 +72,6 @@ module.exports = function manageModeBar(gd) { function getButtonGroups(gd, buttonsToRemove, buttonsToAdd) { var fullLayout = gd._fullLayout, fullData = gd._fullData; - var hasCartesian = fullLayout._has('cartesian'), hasGL3D = fullLayout._has('gl3d'), hasGeo = fullLayout._has('geo'), @@ -121,7 +120,7 @@ function getButtonGroups(gd, buttonsToRemove, buttonsToAdd) { if(((hasCartesian || hasGL2D) && !allAxesFixed) || hasTernary) { dragModeGroup = ['zoom2d', 'pan2d']; } - if((hasCartesian || hasTernary) && isSelectable(fullData)) { + if((hasCartesian || hasTernary || hasGL2D) && isSelectable(fullData)) { dragModeGroup.push('select2d'); dragModeGroup.push('lasso2d'); } @@ -173,7 +172,7 @@ function isSelectable(fullData) { if(!trace._module || !trace._module.selectPoints) continue; - if(trace.type === 'scatter' || trace.type === 'scatterternary') { + if(trace.type === 'scatter' || trace.type === 'scatterternary' || trace.type === 'scattergl') { if(scatterSubTypes.hasMarkers(trace) || scatterSubTypes.hasText(trace)) { selectable = true; } diff --git a/src/traces/scattergl/index.js b/src/traces/scattergl/index.js index d5e71241a46..fa83acd1a5c 100644 --- a/src/traces/scattergl/index.js +++ b/src/traces/scattergl/index.js @@ -17,6 +17,7 @@ ScatterGl.colorbar = require('../scatter/colorbar'); // reuse the Scatter3D 'dummy' calc step so that legends know what to do ScatterGl.calc = require('../scatter3d/calc'); ScatterGl.plot = require('./convert'); +ScatterGl.selectPoints = require('./select'); ScatterGl.moduleType = 'trace'; ScatterGl.name = 'scattergl'; diff --git a/src/traces/scattergl/select.js b/src/traces/scattergl/select.js new file mode 100644 index 00000000000..0ca606219bf --- /dev/null +++ b/src/traces/scattergl/select.js @@ -0,0 +1,73 @@ +/** +* Copyright 2012-2017, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + +// var subtypes = require('./subtypes'); + +var DESELECTDIM = 0.2; + +module.exports = function selectPoints(searchInfo, polygon) { + var cd = searchInfo.cd, + xa = searchInfo.xaxis, + ya = searchInfo.yaxis, + selection = [], + trace = cd[0].trace, + curveNumber = trace.index, + marker = trace.marker, + i, + di, + x, + y; + + console.log('selectPoints', searchInfo, polygon) + return selection; + + // TODO: include lines? that would require per-segment line properties + var hasOnlyLines = (!subtypes.hasMarkers(trace) && !subtypes.hasText(trace)); + if(trace.visible !== true || hasOnlyLines) return; + + var opacity = Array.isArray(marker.opacity) ? 1 : marker.opacity; + + if(polygon === false) { // clear selection + for(i = 0; i < cd.length; i++) cd[i].dim = 0; + } + else { + for(i = 0; i < cd.length; i++) { + di = cd[i]; + x = xa.c2p(di.x); + y = ya.c2p(di.y); + if(polygon.contains([x, y])) { + selection.push({ + curveNumber: curveNumber, + pointNumber: i, + x: di.x, + y: di.y, + id: di.id + }); + di.dim = 0; + } + else di.dim = 1; + } + } + + // do the dimming here, as well as returning the selection + // The logic here duplicates Drawing.pointStyle, but I don't want + // d.dim in pointStyle in case something goes wrong with selection. + cd[0].node3.selectAll('path.point') + .style('opacity', function(d) { + return ((d.mo + 1 || opacity + 1) - 1) * (d.dim ? DESELECTDIM : 1); + }); + cd[0].node3.selectAll('text') + .style('opacity', function(d) { + return d.dim ? DESELECTDIM : 1; + }); + + return selection; +}; From 3d0b325960813febdc54760bb6f1957765a4a368 Mon Sep 17 00:00:00 2001 From: Dmitry Date: Fri, 21 Apr 2017 11:51:57 -0400 Subject: [PATCH 04/61] Fix status --- src/plot_api/plot_api.js | 3 +- src/plots/cartesian/dragbox.js | 3 +- src/plots/cartesian/graph_interact.js | 6 +- src/plots/gl2d/index.js | 213 ++++++++++++++++++++++++-- src/plots/gl2d/scene2d.js | 5 +- src/traces/scattergl/convert.js | 7 +- src/traces/scattergl/select.js | 26 ++-- 7 files changed, 233 insertions(+), 30 deletions(-) diff --git a/src/plot_api/plot_api.js b/src/plot_api/plot_api.js index f25b231cd7f..7a41fcfb0e4 100644 --- a/src/plot_api/plot_api.js +++ b/src/plot_api/plot_api.js @@ -184,11 +184,10 @@ Plotly.plot = function(gd, data, layout, config) { basePlotModules[i].drawFramework(gd); } } - return Lib.syncOrAsync([ subroutines.layoutStyles, drawAxes, - Fx.init + Fx.init, ], gd); } diff --git a/src/plots/cartesian/dragbox.js b/src/plots/cartesian/dragbox.js index 3c174224e60..da5caead192 100644 --- a/src/plots/cartesian/dragbox.js +++ b/src/plots/cartesian/dragbox.js @@ -80,7 +80,7 @@ module.exports = function dragBox(gd, plotinfo, x, y, w, h, ns, ew) { var yIDs = [ya0._id]; // if we're dragging two axes at once, also drag overlays - subplots = [plotinfo].concat((ns && ew) ? plotinfo.overlays : []); + subplots = [plotinfo].concat((ns && ew && plotinfo.overlays) ? plotinfo.overlays : []); for(var i = 1; i < subplots.length; i++) { var subplotXa = subplots[i].xaxis, @@ -171,7 +171,6 @@ module.exports = function dragBox(gd, plotinfo, x, y, w, h, ns, ew) { } } }; - dragElement.init(dragOptions); var x0, diff --git a/src/plots/cartesian/graph_interact.js b/src/plots/cartesian/graph_interact.js index 41e9308288f..7cb198a3041 100644 --- a/src/plots/cartesian/graph_interact.js +++ b/src/plots/cartesian/graph_interact.js @@ -72,7 +72,7 @@ fx.isHoriz = function(fullData) { fx.init = function(gd) { var fullLayout = gd._fullLayout; - if(!fullLayout._has('cartesian') || gd._context.staticPlot) return; + if((!fullLayout._has('cartesian') && !fullLayout._has('gl2d')) || gd._context.staticPlot) return; var subplots = Object.keys(fullLayout._plots || {}).sort(function(a, b) { // sort overlays last, then by x axis number, then y axis number @@ -90,7 +90,7 @@ fx.init = function(gd) { subplots.forEach(function(subplot) { var plotinfo = fullLayout._plots[subplot]; - if(!fullLayout._has('cartesian')) return; + // if(!fullLayout._has('cartesian')) return; var xa = plotinfo.xaxis, ya = plotinfo.yaxis, @@ -113,6 +113,8 @@ fx.init = function(gd) { var maindrag = dragBox(gd, plotinfo, 0, 0, xa._length, ya._length, 'ns', 'ew'); + //FIXME: code below adds corners or somehing + return maindrag.onmousemove = function(evt) { // This is on `gd._fullLayout`, *not* fullLayout because the reference // changes by the time this is called again. diff --git a/src/plots/gl2d/index.js b/src/plots/gl2d/index.js index 2d4f2f7f099..67ed4a71aab 100644 --- a/src/plots/gl2d/index.js +++ b/src/plots/gl2d/index.js @@ -12,7 +12,7 @@ var Scene2D = require('./scene2d'); var Plots = require('../plots'); var xmlnsNamespaces = require('../../constants/xmlns_namespaces'); - +var constants = require('../cartesian/constants') exports.name = 'gl2d'; @@ -20,15 +20,14 @@ exports.attr = ['xaxis', 'yaxis']; exports.idRoot = ['x', 'y']; -exports.idRegex = { - x: /^x([2-9]|[1-9][0-9]+)?$/, - y: /^y([2-9]|[1-9][0-9]+)?$/ -}; +exports.idRegex = constants.idRegex; -exports.attrRegex = { - x: /^xaxis([2-9]|[1-9][0-9]+)?$/, - y: /^yaxis([2-9]|[1-9][0-9]+)?$/ -}; +exports.attrRegex = constants.attrRegex; + +var axisIds = require('../cartesian/axis_ids'); +var Lib = require('../../lib'); +var Plots = require('../plots'); +var d3 = require('d3'); exports.attributes = require('../cartesian/attributes'); @@ -84,6 +83,44 @@ exports.clean = function(newFullData, newFullLayout, oldFullData, oldFullLayout) } }; +exports.drawFramework = function (gd) { + var fullLayout = gd._fullLayout, + subplotData = makeSubplotData(gd); + + var subplotLayers = fullLayout._cartesianlayer.selectAll('.subplot') + .data(subplotData, Lib.identity); + subplotLayers.enter().append('g') + .attr('class', function(name) { return 'subplot ' + name; }); + + subplotLayers.order(); + + subplotLayers.exit() + .call(purgeSubplotLayers, fullLayout); + + subplotLayers.each(function(name) { + var plotinfo = fullLayout._plots[name]; + + // keep ref to plot group + plotinfo.plotgroup = d3.select(this); + + // initialize list of overlay subplots + plotinfo.overlays = []; + + makeSubplotLayer(plotinfo); + + // fill in list of overlay subplots + if(plotinfo.mainplot) { + var mainplot = fullLayout._plots[plotinfo.mainplot]; + mainplot.overlays.push(plotinfo); + } + + // make separate drag layers for each subplot, + // but append them to paper rather than the plot groups, + // so they end up on top of the rest + plotinfo.draglayer = joinLayer(fullLayout._draggers, 'g', name); + }); +} + exports.toSVG = function(gd) { var fullLayout = gd._fullLayout, subplotIds = Plots.getSubplotIds(fullLayout, 'gl2d'); @@ -108,3 +145,161 @@ exports.toSVG = function(gd) { scene.destroy(); } }; + + + + +function makeSubplotData(gd) { + var fullLayout = gd._fullLayout, + subplots = Object.keys(fullLayout._plots); + + var subplotData = [], + overlays = []; + + for(var i = 0; i < subplots.length; i++) { + var subplot = subplots[i], + plotinfo = fullLayout._plots[subplot]; + + var xa = plotinfo.xaxis, + ya = plotinfo.yaxis; + + // is this subplot overlaid on another? + // ax.overlaying is the id of another axis of the same + // dimension that this one overlays to be an overlaid subplot, + // the main plot must exist make sure we're not trying to + // overlay on an axis that's already overlaying another + var xa2 = axisIds.getFromId(gd, xa.overlaying) || xa; + if(xa2 !== xa && xa2.overlaying) { + xa2 = xa; + xa.overlaying = false; + } + + var ya2 = axisIds.getFromId(gd, ya.overlaying) || ya; + if(ya2 !== ya && ya2.overlaying) { + ya2 = ya; + ya.overlaying = false; + } + + var mainplot = xa2._id + ya2._id; + if(mainplot !== subplot && subplots.indexOf(mainplot) !== -1) { + plotinfo.mainplot = mainplot; + plotinfo.mainplotinfo = fullLayout._plots[mainplot]; + overlays.push(subplot); + + // for now force overlays to overlay completely... so they + // can drag together correctly and share backgrounds. + // Later perhaps we make separate axis domain and + // tick/line domain or something, so they can still share + // the (possibly larger) dragger and background but don't + // have to both be drawn over that whole domain + xa.domain = xa2.domain.slice(); + ya.domain = ya2.domain.slice(); + } + else { + subplotData.push(subplot); + } + } + + // main subplots before overlays + subplotData = subplotData.concat(overlays); + + return subplotData; +} + +function makeSubplotLayer(plotinfo) { + var plotgroup = plotinfo.plotgroup, + id = plotinfo.id; + + // Layers to keep plot types in the right order. + // from back to front: + // 1. heatmaps, 2D histos and contour maps + // 2. bars / 1D histos + // 3. errorbars for bars and scatter + // 4. scatter + // 5. box plots + function joinPlotLayers(parent) { + joinLayer(parent, 'g', 'imagelayer'); + joinLayer(parent, 'g', 'maplayer'); + joinLayer(parent, 'g', 'barlayer'); + joinLayer(parent, 'g', 'carpetlayer'); + joinLayer(parent, 'g', 'boxlayer'); + joinLayer(parent, 'g', 'scatterlayer'); + } + + if(!plotinfo.mainplot) { + var backLayer = joinLayer(plotgroup, 'g', 'layer-subplot'); + plotinfo.shapelayer = joinLayer(backLayer, 'g', 'shapelayer'); + plotinfo.imagelayer = joinLayer(backLayer, 'g', 'imagelayer'); + + plotinfo.gridlayer = joinLayer(plotgroup, 'g', 'gridlayer'); + plotinfo.overgrid = joinLayer(plotgroup, 'g', 'overgrid'); + + plotinfo.zerolinelayer = joinLayer(plotgroup, 'g', 'zerolinelayer'); + plotinfo.overzero = joinLayer(plotgroup, 'g', 'overzero'); + + plotinfo.plot = joinLayer(plotgroup, 'g', 'plot'); + plotinfo.overplot = joinLayer(plotgroup, 'g', 'overplot'); + + plotinfo.xlines = joinLayer(plotgroup, 'path', 'xlines'); + plotinfo.ylines = joinLayer(plotgroup, 'path', 'ylines'); + plotinfo.overlines = joinLayer(plotgroup, 'g', 'overlines'); + + plotinfo.xaxislayer = joinLayer(plotgroup, 'g', 'xaxislayer'); + plotinfo.yaxislayer = joinLayer(plotgroup, 'g', 'yaxislayer'); + plotinfo.overaxes = joinLayer(plotgroup, 'g', 'overaxes'); + } + else { + var mainplotinfo = plotinfo.mainplotinfo; + + // now make the components of overlaid subplots + // overlays don't have backgrounds, and append all + // their other components to the corresponding + // extra groups of their main plots. + + plotinfo.gridlayer = joinLayer(mainplotinfo.overgrid, 'g', id); + plotinfo.zerolinelayer = joinLayer(mainplotinfo.overzero, 'g', id); + + plotinfo.plot = joinLayer(mainplotinfo.overplot, 'g', id); + plotinfo.xlines = joinLayer(mainplotinfo.overlines, 'path', id); + plotinfo.ylines = joinLayer(mainplotinfo.overlines, 'path', id); + plotinfo.xaxislayer = joinLayer(mainplotinfo.overaxes, 'g', id); + plotinfo.yaxislayer = joinLayer(mainplotinfo.overaxes, 'g', id); + } + + // common attributes for all subplots, overlays or not + plotinfo.plot.call(joinPlotLayers); + + plotinfo.xlines + .style('fill', 'none') + .classed('crisp', true); + + plotinfo.ylines + .style('fill', 'none') + .classed('crisp', true); +} + +function purgeSubplotLayers(layers, fullLayout) { + if(!layers) return; + + layers.each(function(subplot) { + var plotgroup = d3.select(this), + clipId = 'clip' + fullLayout._uid + subplot + 'plot'; + + plotgroup.remove(); + fullLayout._draggers.selectAll('g.' + subplot).remove(); + fullLayout._defs.select('#' + clipId).remove(); + + // do not remove individual axis s here + // as other subplots may need them + }); +} + +function joinLayer(parent, nodeType, className) { + var layer = parent.selectAll('.' + className) + .data([0]); + + layer.enter().append(nodeType) + .classed(className, true); + + return layer; +} diff --git a/src/plots/gl2d/scene2d.js b/src/plots/gl2d/scene2d.js index 01b1fdb2b56..49eff978e28 100644 --- a/src/plots/gl2d/scene2d.js +++ b/src/plots/gl2d/scene2d.js @@ -152,7 +152,9 @@ proto.makeFramework = function() { var container = this.container; container.appendChild(canvas); container.appendChild(svgContainer); - container.appendChild(mouseContainer); + + //FIXME: since we're going to replace interactions with cartesian plot ones + // container.appendChild(mouseContainer); }; proto.toImage = function(format) { @@ -439,6 +441,7 @@ proto.plot = function(fullData, calcData, fullLayout) { options.dataBox = this.calcDataBox(); options.merge(fullLayout); + glplot.update(options); // force redraw so that promise is returned when rendering is completed diff --git a/src/traces/scattergl/convert.js b/src/traces/scattergl/convert.js index 143f9295446..dc8d91374a4 100644 --- a/src/traces/scattergl/convert.js +++ b/src/traces/scattergl/convert.js @@ -252,7 +252,6 @@ function _convertColor(colors, opacities, count) { } proto.update = function(options) { - if(options.visible !== true) { this.isVisible = false; this.hasLines = false; @@ -638,9 +637,13 @@ proto.dispose = function() { this.fancyScatter.dispose(); }; -function createLineWithMarkers(scene, data) { +function createLineWithMarkers(scene, data, cdscatter) { var plot = new LineWithMarkers(scene, data.uid); plot.update(data); + + //save for selecting points + cdscatter[0].plot = plot + return plot; } diff --git a/src/traces/scattergl/select.js b/src/traces/scattergl/select.js index 0ca606219bf..843145792b5 100644 --- a/src/traces/scattergl/select.js +++ b/src/traces/scattergl/select.js @@ -9,7 +9,7 @@ 'use strict'; -// var subtypes = require('./subtypes'); +var subtypes = require('../scatter/subtypes'); var DESELECTDIM = 0.2; @@ -26,9 +26,6 @@ module.exports = function selectPoints(searchInfo, polygon) { x, y; - console.log('selectPoints', searchInfo, polygon) - return selection; - // TODO: include lines? that would require per-segment line properties var hasOnlyLines = (!subtypes.hasMarkers(trace) && !subtypes.hasText(trace)); if(trace.visible !== true || hasOnlyLines) return; @@ -60,14 +57,19 @@ module.exports = function selectPoints(searchInfo, polygon) { // do the dimming here, as well as returning the selection // The logic here duplicates Drawing.pointStyle, but I don't want // d.dim in pointStyle in case something goes wrong with selection. - cd[0].node3.selectAll('path.point') - .style('opacity', function(d) { - return ((d.mo + 1 || opacity + 1) - 1) * (d.dim ? DESELECTDIM : 1); - }); - cd[0].node3.selectAll('text') - .style('opacity', function(d) { - return d.dim ? DESELECTDIM : 1; - }); + // cd[0].node3.selectAll('path.point') + // .style('opacity', function(d) { + // return ((d.mo + 1 || opacity + 1) - 1) * (d.dim ? DESELECTDIM : 1); + // }); + // cd[0].node3.selectAll('text') + // .style('opacity', function(d) { + // return d.dim ? DESELECTDIM : 1; + // }); + + //TODO: highlight selected points here + var plot = cd[0].plot + cd[0].trace.marker.color = '#000000' + plot.update(cd[0].trace) return selection; }; From e7e4843e52af3994c4abed5df290207acaad1215 Mon Sep 17 00:00:00 2001 From: Dmitry Date: Fri, 21 Apr 2017 17:05:54 -0400 Subject: [PATCH 05/61] Make rezooming work --- src/plots/cartesian/axes.js | 6 +++++- src/plots/cartesian/dragbox.js | 11 ++++++++++- src/plots/cartesian/index.js | 1 - src/plots/cartesian/scale_zoom.js | 1 - src/plots/gl2d/scene2d.js | 2 +- src/traces/scattergl/select.js | 6 +++--- 6 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/plots/cartesian/axes.js b/src/plots/cartesian/axes.js index a362ff0e015..98220586772 100644 --- a/src/plots/cartesian/axes.js +++ b/src/plots/cartesian/axes.js @@ -324,7 +324,11 @@ axes.doAutoRange = function(ax) { var hasDeps = (ax._min && ax._max && ax._min.length && ax._max.length); if(ax.autorange && hasDeps) { - ax.range = axes.getAutoRange(ax); + //FIXME: scattergl call it in async fashion, hence there is .range and _._rl mess + ax.range = ax._rl = axes.getAutoRange(ax); + + ax._r = ax.range.slice(); + ax._rl = Lib.simpleMap(ax._r, ax.r2l); // doAutoRange will get called on fullLayout, // but we want to report its results back to layout diff --git a/src/plots/cartesian/dragbox.js b/src/plots/cartesian/dragbox.js index da5caead192..4add9ea13bb 100644 --- a/src/plots/cartesian/dragbox.js +++ b/src/plots/cartesian/dragbox.js @@ -387,7 +387,9 @@ module.exports = function dragBox(gd, plotinfo, x, y, w, h, ns, ew) { var axRange = Lib.simpleMap(ax.range, ax.r2l), v0 = axRange[0] + (axRange[1] - axRange[0]) * centerFraction; function doZoom(v) { return ax.l2r(v0 + (v - v0) * zoom); } + ax.range = axRange.map(doZoom); + } if(ew || isSubplotConstrained) { @@ -443,7 +445,6 @@ module.exports = function dragBox(gd, plotinfo, x, y, w, h, ns, ew) { } recomputeAxisLists(); - if(xActive === 'ew' || yActive === 'ns') { if(xActive) dragAxList(xa, dx); if(yActive) dragAxList(ya, dy); @@ -504,6 +505,7 @@ module.exports = function dragBox(gd, plotinfo, x, y, w, h, ns, ew) { // scale the other axis the same about its middle for(i = 0; i < xa.length; i++) { xa[i].range = xa[i]._r.slice(); + scaleZoom(xa[i], 1 - dy / ph); } dx = dy * pw / ph; @@ -512,6 +514,7 @@ module.exports = function dragBox(gd, plotinfo, x, y, w, h, ns, ew) { if(!yActive && xActive.length === 1) { for(i = 0; i < ya.length; i++) { ya[i].range = ya[i]._r.slice(); + scaleZoom(ya[i], 1 - dx / pw); } dy = dx * ph / pw; @@ -654,6 +657,7 @@ module.exports = function dragBox(gd, plotinfo, x, y, w, h, ns, ew) { if(axi._r[1] !== axi.range[1]) attrs[axi._name + '.range[1]'] = axi.range[1]; axi.range = axi._input.range = axi._r.slice(); + } updateSubplots([0, 0, pw, ph]); @@ -690,7 +694,9 @@ module.exports = function dragBox(gd, plotinfo, x, y, w, h, ns, ew) { function scaleAndGetShift(ax, scaleFactor) { if(scaleFactor) { + ax.range = ax._r.slice(); + scaleZoom(ax, scaleFactor); return ax._length * (1 - scaleFactor) / 2; } @@ -807,10 +813,12 @@ function zoomAxRanges(axList, r0Fraction, r1Fraction, linkedAxes) { axRangeLinear0 = axi._rl[0]; axRangeLinearSpan = axi._rl[1] - axRangeLinear0; + axi.range = [ axi.l2r(axRangeLinear0 + axRangeLinearSpan * r0Fraction), axi.l2r(axRangeLinear0 + axRangeLinearSpan * r1Fraction) ]; + } // zoom linked axes about their centers @@ -825,6 +833,7 @@ function dragAxList(axList, pix) { for(var i = 0; i < axList.length; i++) { var axi = axList[i]; if(!axi.fixedrange) { + axi.range = [ axi.l2r(axi._rl[0] - pix / axi._m), axi.l2r(axi._rl[1] - pix / axi._m) diff --git a/src/plots/cartesian/index.js b/src/plots/cartesian/index.js index 0649a155296..367f41d2656 100644 --- a/src/plots/cartesian/index.js +++ b/src/plots/cartesian/index.js @@ -281,7 +281,6 @@ function makeSubplotData(gd) { subplotData.push(subplot); } } - // main subplots before overlays subplotData = subplotData.concat(overlays); diff --git a/src/plots/cartesian/scale_zoom.js b/src/plots/cartesian/scale_zoom.js index 7669f742301..90562164b61 100644 --- a/src/plots/cartesian/scale_zoom.js +++ b/src/plots/cartesian/scale_zoom.js @@ -15,7 +15,6 @@ module.exports = function scaleZoom(ax, factor, centerFraction) { var rangeLinear = [ax.r2l(ax.range[0]), ax.r2l(ax.range[1])]; var center = rangeLinear[0] + (rangeLinear[1] - rangeLinear[0]) * centerFraction; var newHalfSpan = (center - rangeLinear[0]) * factor; - ax.range = ax._input.range = [ ax.l2r(center - newHalfSpan), ax.l2r(center + newHalfSpan) diff --git a/src/plots/gl2d/scene2d.js b/src/plots/gl2d/scene2d.js index 49eff978e28..54f51baf5b2 100644 --- a/src/plots/gl2d/scene2d.js +++ b/src/plots/gl2d/scene2d.js @@ -247,6 +247,7 @@ proto.computeTickMarks = function() { // override _length from backward compatibility // even though setScale 'should' give the correct result + this.xaxis._length = this.glplot.viewBox[2] - this.glplot.viewBox[0]; this.yaxis._length = @@ -424,7 +425,6 @@ proto.plot = function(fullData, calcData, fullLayout) { ax = this[AXES[i]]; ax._length = options.viewBox[i + 2] - options.viewBox[i]; - Axes.doAutoRange(ax); ax.setScale(); } diff --git a/src/traces/scattergl/select.js b/src/traces/scattergl/select.js index 843145792b5..51657e48ac9 100644 --- a/src/traces/scattergl/select.js +++ b/src/traces/scattergl/select.js @@ -67,9 +67,9 @@ module.exports = function selectPoints(searchInfo, polygon) { // }); //TODO: highlight selected points here - var plot = cd[0].plot - cd[0].trace.marker.color = '#000000' - plot.update(cd[0].trace) + // var plot = cd[0].plot + // cd[0].trace.marker.color = '#000000' + // plot.update(cd[0].trace) return selection; }; From 6c29193d917711888255c35550286491500ffa23 Mon Sep 17 00:00:00 2001 From: Dmitry Date: Thu, 27 Apr 2017 12:54:33 -0400 Subject: [PATCH 06/61] Get closer to creating scatter for selection --- src/traces/scattergl/convert.js | 1 + src/traces/scattergl/select.js | 1 + 2 files changed, 2 insertions(+) diff --git a/src/traces/scattergl/convert.js b/src/traces/scattergl/convert.js index dc8d91374a4..88f07bffd0f 100644 --- a/src/traces/scattergl/convert.js +++ b/src/traces/scattergl/convert.js @@ -100,6 +100,7 @@ function LineWithMarkers(scene, uid) { this.scatter = this.initObject(createScatter, scatterOptions0, 3); this.fancyScatter = this.initObject(createFancyScatter, scatterOptions0, 4); + this.selectedScatter = this.initObject(createScatter, scatterOptions0, 5) } var proto = LineWithMarkers.prototype; diff --git a/src/traces/scattergl/select.js b/src/traces/scattergl/select.js index 51657e48ac9..ee3c6ee3d85 100644 --- a/src/traces/scattergl/select.js +++ b/src/traces/scattergl/select.js @@ -70,6 +70,7 @@ module.exports = function selectPoints(searchInfo, polygon) { // var plot = cd[0].plot // cd[0].trace.marker.color = '#000000' // plot.update(cd[0].trace) + console.log(cd[0].plot) return selection; }; From 6d4f9a59fe9cff4d5cd6d40ce2d21feee9d9030a Mon Sep 17 00:00:00 2001 From: Dmitry Date: Thu, 27 Apr 2017 16:06:16 -0400 Subject: [PATCH 07/61] Fix progress --- src/plots/gl2d/scene2d.js | 11 ---------- src/traces/scattergl/convert.js | 36 ++++++++++++++++++++++++++------- src/traces/scattergl/select.js | 5 ++--- 3 files changed, 31 insertions(+), 21 deletions(-) diff --git a/src/plots/gl2d/scene2d.js b/src/plots/gl2d/scene2d.js index 54f51baf5b2..5f955c9d719 100644 --- a/src/plots/gl2d/scene2d.js +++ b/src/plots/gl2d/scene2d.js @@ -152,9 +152,6 @@ proto.makeFramework = function() { var container = this.container; container.appendChild(canvas); container.appendChild(svgContainer); - - //FIXME: since we're going to replace interactions with cartesian plot ones - // container.appendChild(mouseContainer); }; proto.toImage = function(format) { @@ -326,7 +323,6 @@ proto.cameraChanged = function() { var nextTicks = this.computeTickMarks(); var curTicks = this.glplotOptions.ticks; - if(compareTicks(nextTicks, curTicks)) { this.glplotOptions.ticks = nextTicks; this.glplotOptions.dataBox = camera.dataBox; @@ -362,7 +358,6 @@ proto.destroy = function() { if(!this.staticPlot) this.container.removeChild(this.canvas); this.container.removeChild(this.svgContainer); - this.container.removeChild(this.mouseContainer); this.fullData = null; this.glplot = null; @@ -395,12 +390,6 @@ proto.plot = function(fullData, calcData, fullLayout) { (height - size.t) - (1 - domainY[1]) * size.h ]; - this.mouseContainer.style.width = size.w * (domainX[1] - domainX[0]) + 'px'; - this.mouseContainer.style.height = size.h * (domainY[1] - domainY[0]) + 'px'; - this.mouseContainer.height = size.h * (domainY[1] - domainY[0]); - this.mouseContainer.style.left = size.l + domainX[0] * size.w + 'px'; - this.mouseContainer.style.top = size.t + (1 - domainY[1]) * size.h + 'px'; - var bounds = this.bounds; bounds[0] = bounds[1] = Infinity; bounds[2] = bounds[3] = -Infinity; diff --git a/src/traces/scattergl/convert.js b/src/traces/scattergl/convert.js index 88f07bffd0f..23832f94329 100644 --- a/src/traces/scattergl/convert.js +++ b/src/traces/scattergl/convert.js @@ -100,7 +100,7 @@ function LineWithMarkers(scene, uid) { this.scatter = this.initObject(createScatter, scatterOptions0, 3); this.fancyScatter = this.initObject(createFancyScatter, scatterOptions0, 4); - this.selectedScatter = this.initObject(createScatter, scatterOptions0, 5) + this.selectScatter = this.initObject(createScatter, scatterOptions0, 5) } var proto = LineWithMarkers.prototype; @@ -383,23 +383,45 @@ proto.updateFast = function(options) { var markerSize; if(this.hasMarkers) { - this.scatter.options.positions = positions; + // split positions into selected/non-selected ones + // this.scatter.options.positions = positions; + + // var markerColor = str2RGBArray(options.marker.color), + // borderColor = str2RGBArray(options.marker.line.color), + // opacity = (options.opacity) * (options.marker.opacity); + + // markerColor[3] *= opacity; + // this.scatter.options.color = markerColor; + + // borderColor[3] *= opacity; + // this.scatter.options.borderColor = borderColor; + + // markerSize = options.marker.size; + // this.scatter.options.size = markerSize; + // this.scatter.options.borderSize = options.marker.line.width; + + // this.scatter.update(); + + + + + this.selectScatter.options.positions = positions; var markerColor = str2RGBArray(options.marker.color), borderColor = str2RGBArray(options.marker.line.color), opacity = (options.opacity) * (options.marker.opacity); markerColor[3] *= opacity; - this.scatter.options.color = markerColor; + this.selectScatter.options.color = markerColor; borderColor[3] *= opacity; - this.scatter.options.borderColor = borderColor; + this.selectScatter.options.borderColor = borderColor; markerSize = options.marker.size; - this.scatter.options.size = markerSize; - this.scatter.options.borderSize = options.marker.line.width; + this.selectScatter.options.size = markerSize; + this.selectScatter.options.borderSize = options.marker.line.width; - this.scatter.update(); + this.selectScatter.update(); } else { this.scatter.clear(); diff --git a/src/traces/scattergl/select.js b/src/traces/scattergl/select.js index ee3c6ee3d85..8d9568f07a3 100644 --- a/src/traces/scattergl/select.js +++ b/src/traces/scattergl/select.js @@ -67,10 +67,9 @@ module.exports = function selectPoints(searchInfo, polygon) { // }); //TODO: highlight selected points here - // var plot = cd[0].plot + var plot = cd[0].plot // cd[0].trace.marker.color = '#000000' - // plot.update(cd[0].trace) - console.log(cd[0].plot) + // plot.update() return selection; }; From cf73fbe496ed52609ab041aadaccc784af5f89ae Mon Sep 17 00:00:00 2001 From: Dmitry Date: Fri, 28 Apr 2017 14:23:18 -0400 Subject: [PATCH 08/61] Fix intermediate progress --- src/traces/scattergl/convert.js | 2 -- src/traces/scattergl/select.js | 27 ++++++++++++++++++--------- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/src/traces/scattergl/convert.js b/src/traces/scattergl/convert.js index 23832f94329..f057f4cd791 100644 --- a/src/traces/scattergl/convert.js +++ b/src/traces/scattergl/convert.js @@ -404,7 +404,6 @@ proto.updateFast = function(options) { - this.selectScatter.options.positions = positions; var markerColor = str2RGBArray(options.marker.color), @@ -420,7 +419,6 @@ proto.updateFast = function(options) { markerSize = options.marker.size; this.selectScatter.options.size = markerSize; this.selectScatter.options.borderSize = options.marker.line.width; - this.selectScatter.update(); } else { diff --git a/src/traces/scattergl/select.js b/src/traces/scattergl/select.js index 8d9568f07a3..77b422d5758 100644 --- a/src/traces/scattergl/select.js +++ b/src/traces/scattergl/select.js @@ -57,19 +57,28 @@ module.exports = function selectPoints(searchInfo, polygon) { // do the dimming here, as well as returning the selection // The logic here duplicates Drawing.pointStyle, but I don't want // d.dim in pointStyle in case something goes wrong with selection. - // cd[0].node3.selectAll('path.point') - // .style('opacity', function(d) { - // return ((d.mo + 1 || opacity + 1) - 1) * (d.dim ? DESELECTDIM : 1); - // }); - // cd[0].node3.selectAll('text') - // .style('opacity', function(d) { - // return d.dim ? DESELECTDIM : 1; - // }); + cd[0].node3.selectAll('path.point') + .style('opacity', function(d) { + return ((d.mo + 1 || opacity + 1) - 1) * (d.dim ? DESELECTDIM : 1); + }); + cd[0].node3.selectAll('text') + .style('opacity', function(d) { + return d.dim ? DESELECTDIM : 1; + }); //TODO: highlight selected points here var plot = cd[0].plot + var scene = plot.scene + var options = scene.fullData[0] + + //modify the full data + //FIXME: remove + options.x = options.x.slice(1) + options.y = options.y.slice(1) + // cd[0].trace.marker.color = '#000000' - // plot.update() + plot.update(options) + // scene.draw() return selection; }; From d3427c0bef1f8c75968015f7c683e75c6d35da74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Fri, 28 Apr 2017 14:45:06 -0400 Subject: [PATCH 09/61] first working thing --- src/traces/scattergl/select.js | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/src/traces/scattergl/select.js b/src/traces/scattergl/select.js index 77b422d5758..6d3a7d29bb3 100644 --- a/src/traces/scattergl/select.js +++ b/src/traces/scattergl/select.js @@ -57,28 +57,29 @@ module.exports = function selectPoints(searchInfo, polygon) { // do the dimming here, as well as returning the selection // The logic here duplicates Drawing.pointStyle, but I don't want // d.dim in pointStyle in case something goes wrong with selection. - cd[0].node3.selectAll('path.point') - .style('opacity', function(d) { - return ((d.mo + 1 || opacity + 1) - 1) * (d.dim ? DESELECTDIM : 1); - }); - cd[0].node3.selectAll('text') - .style('opacity', function(d) { - return d.dim ? DESELECTDIM : 1; - }); +// cd[0].node3.selectAll('path.point') +// .style('opacity', function(d) { +// return ((d.mo + 1 || opacity + 1) - 1) * (d.dim ? DESELECTDIM : 1); +// }); +// cd[0].node3.selectAll('text') +// .style('opacity', function(d) { +// return d.dim ? DESELECTDIM : 1; +// }); //TODO: highlight selected points here - var plot = cd[0].plot - var scene = plot.scene - var options = scene.fullData[0] + var scene = cd[0].plot.scene; + var fullTrace = cd[0].trace; //modify the full data //FIXME: remove - options.x = options.x.slice(1) - options.y = options.y.slice(1) - + fullTrace.x = fullTrace.x.slice(1) + fullTrace.y = fullTrace.y.slice(1) + // cd[0].trace.marker.color = '#000000' - plot.update(options) - // scene.draw() + scene.plot([fullTrace], [cd], scene.fullLayout); + + /// maybe this will work too? + // scene.plot(scene.fullData, scene.calcdata, scene.fullLayout); return selection; }; From 190d3c7216388816adc3124d10546dc4835d1e8b Mon Sep 17 00:00:00 2001 From: Dmitry Date: Fri, 28 Apr 2017 16:53:04 -0400 Subject: [PATCH 10/61] Add proper points limits --- src/traces/scattergl/index.js | 2 +- src/traces/scattergl/select.js | 15 +++++++++------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/traces/scattergl/index.js b/src/traces/scattergl/index.js index fa83acd1a5c..8d0e1a40caf 100644 --- a/src/traces/scattergl/index.js +++ b/src/traces/scattergl/index.js @@ -15,7 +15,7 @@ ScatterGl.supplyDefaults = require('./defaults'); ScatterGl.colorbar = require('../scatter/colorbar'); // reuse the Scatter3D 'dummy' calc step so that legends know what to do -ScatterGl.calc = require('../scatter3d/calc'); +ScatterGl.calc = require('../scatter/calc'); ScatterGl.plot = require('./convert'); ScatterGl.selectPoints = require('./select'); diff --git a/src/traces/scattergl/select.js b/src/traces/scattergl/select.js index 6d3a7d29bb3..e2855c2609a 100644 --- a/src/traces/scattergl/select.js +++ b/src/traces/scattergl/select.js @@ -66,20 +66,23 @@ module.exports = function selectPoints(searchInfo, polygon) { // return d.dim ? DESELECTDIM : 1; // }); + + //TODO: highlight selected points here + var traceObj = cd[0].plot; var scene = cd[0].plot.scene; var fullTrace = cd[0].trace; - //modify the full data //FIXME: remove fullTrace.x = fullTrace.x.slice(1) fullTrace.y = fullTrace.y.slice(1) - - // cd[0].trace.marker.color = '#000000' - scene.plot([fullTrace], [cd], scene.fullLayout); - /// maybe this will work too? - // scene.plot(scene.fullData, scene.calcdata, scene.fullLayout); + + // scene.plot([fullTrace], [cd], scene.fullLayout); + + // excerpt from ↑ + traceObj.update(fullTrace, cd) + scene.glplot.setDirty() return selection; }; From 9921fb979916eb6195de67002a20631eb695dc17 Mon Sep 17 00:00:00 2001 From: Dmitry Date: Wed, 3 May 2017 14:51:29 -0400 Subject: [PATCH 11/61] Add scattergl selection --- src/traces/scattergl/convert.js | 94 +++++++++++++++++++++++---------- src/traces/scattergl/select.js | 5 +- 2 files changed, 70 insertions(+), 29 deletions(-) diff --git a/src/traces/scattergl/convert.js b/src/traces/scattergl/convert.js index f057f4cd791..f39d7e784c7 100644 --- a/src/traces/scattergl/convert.js +++ b/src/traces/scattergl/convert.js @@ -29,6 +29,7 @@ var MARKER_SYMBOLS = require('../../constants/gl_markers'); var DASHES = require('../../constants/gl2d_dashes'); var AXES = ['xaxis', 'yaxis']; +var DESELECTDIM = 0.2; function LineWithMarkers(scene, uid) { @@ -252,7 +253,7 @@ function _convertColor(colors, opacities, count) { return result; } -proto.update = function(options) { +proto.update = function(options, cd) { if(options.visible !== true) { this.isVisible = false; this.hasLines = false; @@ -376,50 +377,89 @@ proto.updateFast = function(options) { positions = truncate(positions, ptr); this.idToIndex = idToIndex; + //form selected set + var selPositions + if (options.selection) { + let selection = options.selection + selPositions = new Float64Array(2 * options.selection.length) + + for (var i = 0, l = selection.length; i < l; i++) { + selPositions[i * 2 + 0] = selection[i].x + selPositions[i * 2 + 1] = selection[i].y + } + } + this.updateLines(options, positions); this.updateError('X', options); this.updateError('Y', options); var markerSize; - if(this.hasMarkers) { - // split positions into selected/non-selected ones - // this.scatter.options.positions = positions; + if (this.hasMarkers) { + //if we have selPositions array - means we have to render all as transparent, and selection as opaque + //FIXME: optimize this code part + if (selPositions) { + + this.selectScatter.options.positions = positions; + + var markerColor = str2RGBArray(options.marker.color), + borderColor = str2RGBArray(options.marker.line.color), + opacity = (options.opacity) * (options.marker.opacity) * DESELECTDIM; + + markerColor[3] *= opacity; + this.selectScatter.options.color = markerColor; - // var markerColor = str2RGBArray(options.marker.color), - // borderColor = str2RGBArray(options.marker.line.color), - // opacity = (options.opacity) * (options.marker.opacity); + borderColor[3] *= opacity; + this.selectScatter.options.borderColor = borderColor; - // markerColor[3] *= opacity; - // this.scatter.options.color = markerColor; + markerSize = options.marker.size; + this.selectScatter.options.size = markerSize; + this.selectScatter.options.borderSize = options.marker.line.width; + this.selectScatter.update(); - // borderColor[3] *= opacity; - // this.scatter.options.borderColor = borderColor; - // markerSize = options.marker.size; - // this.scatter.options.size = markerSize; - // this.scatter.options.borderSize = options.marker.line.width; + // split positions into selected/non-selected ones + this.scatter.options.positions = selPositions; - // this.scatter.update(); + var markerColor = str2RGBArray(options.marker.color), + borderColor = str2RGBArray(options.marker.line.color), + opacity = (options.opacity) * (options.marker.opacity); + markerColor[3] *= opacity; + this.scatter.options.color = markerColor; + borderColor[3] *= opacity; + this.scatter.options.borderColor = borderColor; - this.selectScatter.options.positions = positions; + markerSize = options.marker.size; + this.scatter.options.size = markerSize; + this.scatter.options.borderSize = options.marker.line.width; - var markerColor = str2RGBArray(options.marker.color), - borderColor = str2RGBArray(options.marker.line.color), - opacity = (options.opacity) * (options.marker.opacity); + this.scatter.update(); + } + + else { + this.scatter.options.positions = positions; + + var markerColor = str2RGBArray(options.marker.color), + borderColor = str2RGBArray(options.marker.line.color), + opacity = (options.opacity) * (options.marker.opacity); + + markerColor[3] *= opacity; + this.scatter.options.color = markerColor; + + borderColor[3] *= opacity; + this.scatter.options.borderColor = borderColor; + + markerSize = options.marker.size; + this.scatter.options.size = markerSize; + this.scatter.options.borderSize = options.marker.line.width; + + this.scatter.update(); + } - markerColor[3] *= opacity; - this.selectScatter.options.color = markerColor; - borderColor[3] *= opacity; - this.selectScatter.options.borderColor = borderColor; - markerSize = options.marker.size; - this.selectScatter.options.size = markerSize; - this.selectScatter.options.borderSize = options.marker.line.width; - this.selectScatter.update(); } else { this.scatter.clear(); diff --git a/src/traces/scattergl/select.js b/src/traces/scattergl/select.js index e2855c2609a..ffd23c444dc 100644 --- a/src/traces/scattergl/select.js +++ b/src/traces/scattergl/select.js @@ -74,9 +74,10 @@ module.exports = function selectPoints(searchInfo, polygon) { var fullTrace = cd[0].trace; //FIXME: remove - fullTrace.x = fullTrace.x.slice(1) - fullTrace.y = fullTrace.y.slice(1) + // fullTrace.x = fullTrace.x.slice(1) + // fullTrace.y = fullTrace.y.slice(1) + fullTrace.selection = selection // scene.plot([fullTrace], [cd], scene.fullLayout); From 4bff01346ce2e8af39d3a1355f48d32d7d7e524e Mon Sep 17 00:00:00 2001 From: Dmitry Date: Wed, 3 May 2017 15:03:03 -0400 Subject: [PATCH 12/61] Fix MVP implementation --- src/traces/scattergl/convert.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/traces/scattergl/convert.js b/src/traces/scattergl/convert.js index f39d7e784c7..c8738b79383 100644 --- a/src/traces/scattergl/convert.js +++ b/src/traces/scattergl/convert.js @@ -29,7 +29,7 @@ var MARKER_SYMBOLS = require('../../constants/gl_markers'); var DASHES = require('../../constants/gl2d_dashes'); var AXES = ['xaxis', 'yaxis']; -var DESELECTDIM = 0.2; +var DESELECTDIM = 0.002; function LineWithMarkers(scene, uid) { From ab07db397a74f011d28cff5f50d5c1cfa37692ab Mon Sep 17 00:00:00 2001 From: Dmitry Date: Mon, 8 May 2017 14:38:10 -0400 Subject: [PATCH 13/61] Fix proper points filtering --- src/traces/scattergl/convert.js | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/src/traces/scattergl/convert.js b/src/traces/scattergl/convert.js index c8738b79383..55cf3da67db 100644 --- a/src/traces/scattergl/convert.js +++ b/src/traces/scattergl/convert.js @@ -29,7 +29,7 @@ var MARKER_SYMBOLS = require('../../constants/gl_markers'); var DASHES = require('../../constants/gl2d_dashes'); var AXES = ['xaxis', 'yaxis']; -var DESELECTDIM = 0.002; +var DESELECTDIM = 0.2; function LineWithMarkers(scene, uid) { @@ -338,7 +338,18 @@ proto.updateFast = function(options) { positions = new Float64Array(2 * len), bounds = this.bounds, pId = 0, - ptr = 0; + ptr = 0, + selection = options.selection, + sel; + + //form selection cache + if (selection) { + sel = {} + for (var i = 0, l = selection.length; i < l; i++) { + var pt = selection[i] + sel[pt.id] = pt + } + } var xx, yy; @@ -361,10 +372,14 @@ proto.updateFast = function(options) { xx = Lib.dateTime2ms(xx, xcalendar); } + // ignore selected points from positions + if (!sel || !sel[i]) { + positions[ptr++] = xx; + positions[ptr++] = yy; + } + idToIndex[pId++] = i; - positions[ptr++] = xx; - positions[ptr++] = yy; bounds[0] = Math.min(bounds[0], xx); bounds[1] = Math.min(bounds[1], yy); @@ -379,9 +394,8 @@ proto.updateFast = function(options) { //form selected set var selPositions - if (options.selection) { - let selection = options.selection - selPositions = new Float64Array(2 * options.selection.length) + if (selection) { + selPositions = new Float64Array(2 * selection.length) for (var i = 0, l = selection.length; i < l; i++) { selPositions[i * 2 + 0] = selection[i].x @@ -396,10 +410,8 @@ proto.updateFast = function(options) { var markerSize; if (this.hasMarkers) { - //if we have selPositions array - means we have to render all as transparent, and selection as opaque - //FIXME: optimize this code part + // if we have selPositions array - means we have to render all points transparent, and selected points opaque if (selPositions) { - this.selectScatter.options.positions = positions; var markerColor = str2RGBArray(options.marker.color), @@ -418,7 +430,6 @@ proto.updateFast = function(options) { this.selectScatter.update(); - // split positions into selected/non-selected ones this.scatter.options.positions = selPositions; var markerColor = str2RGBArray(options.marker.color), From 1a3ce6d591639dca8f62012cc3167c78ce0a5cfa Mon Sep 17 00:00:00 2001 From: Dmitry Date: Mon, 8 May 2017 15:21:20 -0400 Subject: [PATCH 14/61] Add selection to scattergl-fancy --- src/traces/scattergl/convert.js | 31 +++++++++++++++++++------------ src/traces/scattergl/select.js | 1 + 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/src/traces/scattergl/convert.js b/src/traces/scattergl/convert.js index 55cf3da67db..631f04ec01c 100644 --- a/src/traces/scattergl/convert.js +++ b/src/traces/scattergl/convert.js @@ -275,6 +275,17 @@ proto.update = function(options, cd) { this.bounds = [Infinity, Infinity, -Infinity, -Infinity]; this.connectgaps = !!options.connectgaps; + //form selection cache + var sel, selection = options.selection + if (selection) { + sel = {} + for (var i = 0, l = selection.length; i < l; i++) { + var pt = selection[i] + sel[pt.pointNumber] = pt + } + this.selectedIds = sel + } + if(!this.isVisible) { this.line.clear(); this.errorX.clear(); @@ -340,16 +351,7 @@ proto.updateFast = function(options) { pId = 0, ptr = 0, selection = options.selection, - sel; - - //form selection cache - if (selection) { - sel = {} - for (var i = 0, l = selection.length; i < l; i++) { - var pt = selection[i] - sel[pt.id] = pt - } - } + sel = this.selectedIds; var xx, yy; @@ -487,7 +489,8 @@ proto.updateFancy = function(options) { var scene = this.scene, xaxis = scene.xaxis, yaxis = scene.yaxis, - bounds = this.bounds; + bounds = this.bounds, + sel = this.selectedIds // makeCalcdata runs d2c (data-to-coordinate) on every point var x = this.pickXData = xaxis.makeCalcdata(options, 'x').slice(); @@ -582,7 +585,11 @@ proto.updateFancy = function(options) { this.scatter.options.borderWidths[i] = 0.5 * borderWidths[index]; for(j = 0; j < 4; ++j) { - this.scatter.options.colors[4 * i + j] = colors[4 * index + j]; + var color = colors[4 * index + j]; + if (sel && sel[index] && j === 3) { + color *= DESELECTDIM + } + this.scatter.options.colors[4 * i + j] = color; this.scatter.options.borderColors[4 * i + j] = borderColors[4 * index + j]; } } diff --git a/src/traces/scattergl/select.js b/src/traces/scattergl/select.js index ffd23c444dc..275721ca21e 100644 --- a/src/traces/scattergl/select.js +++ b/src/traces/scattergl/select.js @@ -46,6 +46,7 @@ module.exports = function selectPoints(searchInfo, polygon) { pointNumber: i, x: di.x, y: di.y, + //FIXME: di.id is undefined for scattergls id: di.id }); di.dim = 0; From eb4654d3a626d130e8b06c37f4e71dd8b5ade74c Mon Sep 17 00:00:00 2001 From: Dmitry Date: Mon, 8 May 2017 19:25:32 -0400 Subject: [PATCH 15/61] Make fancy scatter properly select points --- src/traces/scattergl/convert.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/traces/scattergl/convert.js b/src/traces/scattergl/convert.js index 631f04ec01c..fd53a9e8250 100644 --- a/src/traces/scattergl/convert.js +++ b/src/traces/scattergl/convert.js @@ -586,7 +586,7 @@ proto.updateFancy = function(options) { for(j = 0; j < 4; ++j) { var color = colors[4 * index + j]; - if (sel && sel[index] && j === 3) { + if (sel && !sel[index] && j === 3) { color *= DESELECTDIM } this.scatter.options.colors[4 * i + j] = color; From 920127dfa318e938a63ae9fbfd41eba7c990b92d Mon Sep 17 00:00:00 2001 From: Dmitry Date: Wed, 10 May 2017 11:01:50 -0400 Subject: [PATCH 16/61] Lintify --- src/plots/cartesian/axes.js | 2 +- src/plots/cartesian/graph_interact.js | 2 - src/plots/gl2d/index.js | 9 ++-- src/traces/scattergl/convert.js | 76 +++++++++++++-------------- src/traces/scattergl/select.js | 33 ++---------- 5 files changed, 47 insertions(+), 75 deletions(-) diff --git a/src/plots/cartesian/axes.js b/src/plots/cartesian/axes.js index 98220586772..cc3e8160dce 100644 --- a/src/plots/cartesian/axes.js +++ b/src/plots/cartesian/axes.js @@ -324,7 +324,7 @@ axes.doAutoRange = function(ax) { var hasDeps = (ax._min && ax._max && ax._min.length && ax._max.length); if(ax.autorange && hasDeps) { - //FIXME: scattergl call it in async fashion, hence there is .range and _._rl mess + // FIXME: scattergl calls it in async fashion, hence there is .range and _._rl mess ax.range = ax._rl = axes.getAutoRange(ax); ax._r = ax.range.slice(); diff --git a/src/plots/cartesian/graph_interact.js b/src/plots/cartesian/graph_interact.js index 7cb198a3041..dec0b24c748 100644 --- a/src/plots/cartesian/graph_interact.js +++ b/src/plots/cartesian/graph_interact.js @@ -113,8 +113,6 @@ fx.init = function(gd) { var maindrag = dragBox(gd, plotinfo, 0, 0, xa._length, ya._length, 'ns', 'ew'); - //FIXME: code below adds corners or somehing - return maindrag.onmousemove = function(evt) { // This is on `gd._fullLayout`, *not* fullLayout because the reference // changes by the time this is called again. diff --git a/src/plots/gl2d/index.js b/src/plots/gl2d/index.js index 67ed4a71aab..e6f491823c7 100644 --- a/src/plots/gl2d/index.js +++ b/src/plots/gl2d/index.js @@ -12,7 +12,7 @@ var Scene2D = require('./scene2d'); var Plots = require('../plots'); var xmlnsNamespaces = require('../../constants/xmlns_namespaces'); -var constants = require('../cartesian/constants') +var constants = require('../cartesian/constants'); exports.name = 'gl2d'; @@ -26,7 +26,6 @@ exports.attrRegex = constants.attrRegex; var axisIds = require('../cartesian/axis_ids'); var Lib = require('../../lib'); -var Plots = require('../plots'); var d3 = require('d3'); exports.attributes = require('../cartesian/attributes'); @@ -83,7 +82,7 @@ exports.clean = function(newFullData, newFullLayout, oldFullData, oldFullLayout) } }; -exports.drawFramework = function (gd) { +exports.drawFramework = function(gd) { var fullLayout = gd._fullLayout, subplotData = makeSubplotData(gd); @@ -119,7 +118,7 @@ exports.drawFramework = function (gd) { // so they end up on top of the rest plotinfo.draglayer = joinLayer(fullLayout._draggers, 'g', name); }); -} +}; exports.toSVG = function(gd) { var fullLayout = gd._fullLayout, @@ -147,8 +146,6 @@ exports.toSVG = function(gd) { }; - - function makeSubplotData(gd) { var fullLayout = gd._fullLayout, subplots = Object.keys(fullLayout._plots); diff --git a/src/traces/scattergl/convert.js b/src/traces/scattergl/convert.js index fd53a9e8250..5d72121e01d 100644 --- a/src/traces/scattergl/convert.js +++ b/src/traces/scattergl/convert.js @@ -101,7 +101,7 @@ function LineWithMarkers(scene, uid) { this.scatter = this.initObject(createScatter, scatterOptions0, 3); this.fancyScatter = this.initObject(createFancyScatter, scatterOptions0, 4); - this.selectScatter = this.initObject(createScatter, scatterOptions0, 5) + this.selectScatter = this.initObject(createScatter, scatterOptions0, 5); } var proto = LineWithMarkers.prototype; @@ -253,7 +253,7 @@ function _convertColor(colors, opacities, count) { return result; } -proto.update = function(options, cd) { +proto.update = function(options) { if(options.visible !== true) { this.isVisible = false; this.hasLines = false; @@ -275,15 +275,15 @@ proto.update = function(options, cd) { this.bounds = [Infinity, Infinity, -Infinity, -Infinity]; this.connectgaps = !!options.connectgaps; - //form selection cache - var sel, selection = options.selection - if (selection) { - sel = {} - for (var i = 0, l = selection.length; i < l; i++) { - var pt = selection[i] - sel[pt.pointNumber] = pt + // form selection cache + var sel, selection = options.selection; + if(selection) { + sel = {}; + for(var i = 0, l = selection.length; i < l; i++) { + var pt = selection[i]; + sel[pt.pointNumber] = pt; } - this.selectedIds = sel + this.selectedIds = sel; } if(!this.isVisible) { @@ -351,7 +351,8 @@ proto.updateFast = function(options) { pId = 0, ptr = 0, selection = options.selection, - sel = this.selectedIds; + sel = this.selectedIds, + i, selPositions, l; var xx, yy; @@ -364,7 +365,7 @@ proto.updateFast = function(options) { // TODO bypass this on modebar +/- zoom if(fastType || isDateTime) { - for(var i = 0; i < len; ++i) { + for(i = 0; i < len; ++i) { xx = x[i]; yy = y[i]; @@ -375,7 +376,7 @@ proto.updateFast = function(options) { } // ignore selected points from positions - if (!sel || !sel[i]) { + if(!sel || !sel[i]) { positions[ptr++] = xx; positions[ptr++] = yy; } @@ -394,14 +395,13 @@ proto.updateFast = function(options) { positions = truncate(positions, ptr); this.idToIndex = idToIndex; - //form selected set - var selPositions - if (selection) { - selPositions = new Float64Array(2 * selection.length) + // form selected set + if(selection) { + selPositions = new Float64Array(2 * selection.length); - for (var i = 0, l = selection.length; i < l; i++) { - selPositions[i * 2 + 0] = selection[i].x - selPositions[i * 2 + 1] = selection[i].y + for(i = 0, l = selection.length; i < l; i++) { + selPositions[i * 2 + 0] = selection[i].x; + selPositions[i * 2 + 1] = selection[i].y; } } @@ -411,14 +411,16 @@ proto.updateFast = function(options) { var markerSize; - if (this.hasMarkers) { + if(this.hasMarkers) { + var markerColor, borderColor, opacity; + // if we have selPositions array - means we have to render all points transparent, and selected points opaque - if (selPositions) { + if(selPositions) { this.selectScatter.options.positions = positions; - var markerColor = str2RGBArray(options.marker.color), - borderColor = str2RGBArray(options.marker.line.color), - opacity = (options.opacity) * (options.marker.opacity) * DESELECTDIM; + markerColor = str2RGBArray(options.marker.color); + borderColor = str2RGBArray(options.marker.line.color); + opacity = (options.opacity) * (options.marker.opacity) * DESELECTDIM; markerColor[3] *= opacity; this.selectScatter.options.color = markerColor; @@ -434,9 +436,9 @@ proto.updateFast = function(options) { this.scatter.options.positions = selPositions; - var markerColor = str2RGBArray(options.marker.color), - borderColor = str2RGBArray(options.marker.line.color), - opacity = (options.opacity) * (options.marker.opacity); + markerColor = str2RGBArray(options.marker.color); + borderColor = str2RGBArray(options.marker.line.color); + opacity = (options.opacity) * (options.marker.opacity); markerColor[3] *= opacity; this.scatter.options.color = markerColor; @@ -454,9 +456,9 @@ proto.updateFast = function(options) { else { this.scatter.options.positions = positions; - var markerColor = str2RGBArray(options.marker.color), - borderColor = str2RGBArray(options.marker.line.color), - opacity = (options.opacity) * (options.marker.opacity); + markerColor = str2RGBArray(options.marker.color); + borderColor = str2RGBArray(options.marker.line.color); + opacity = (options.opacity) * (options.marker.opacity); markerColor[3] *= opacity; this.scatter.options.color = markerColor; @@ -471,8 +473,6 @@ proto.updateFast = function(options) { this.scatter.update(); } - - } else { this.scatter.clear(); @@ -490,7 +490,7 @@ proto.updateFancy = function(options) { xaxis = scene.xaxis, yaxis = scene.yaxis, bounds = this.bounds, - sel = this.selectedIds + sel = this.selectedIds; // makeCalcdata runs d2c (data-to-coordinate) on every point var x = this.pickXData = xaxis.makeCalcdata(options, 'x').slice(); @@ -586,8 +586,8 @@ proto.updateFancy = function(options) { for(j = 0; j < 4; ++j) { var color = colors[4 * index + j]; - if (sel && !sel[index] && j === 3) { - color *= DESELECTDIM + if(sel && !sel[index] && j === 3) { + color *= DESELECTDIM; } this.scatter.options.colors[4 * i + j] = color; this.scatter.options.borderColors[4 * i + j] = borderColors[4 * index + j]; @@ -720,8 +720,8 @@ function createLineWithMarkers(scene, data, cdscatter) { var plot = new LineWithMarkers(scene, data.uid); plot.update(data); - //save for selecting points - cdscatter[0].plot = plot + // save for selecting points + cdscatter[0].plot = plot; return plot; } diff --git a/src/traces/scattergl/select.js b/src/traces/scattergl/select.js index 275721ca21e..48a11126879 100644 --- a/src/traces/scattergl/select.js +++ b/src/traces/scattergl/select.js @@ -11,8 +11,6 @@ var subtypes = require('../scatter/subtypes'); -var DESELECTDIM = 0.2; - module.exports = function selectPoints(searchInfo, polygon) { var cd = searchInfo.cd, xa = searchInfo.xaxis, @@ -20,7 +18,6 @@ module.exports = function selectPoints(searchInfo, polygon) { selection = [], trace = cd[0].trace, curveNumber = trace.index, - marker = trace.marker, i, di, x, @@ -30,8 +27,6 @@ module.exports = function selectPoints(searchInfo, polygon) { var hasOnlyLines = (!subtypes.hasMarkers(trace) && !subtypes.hasText(trace)); if(trace.visible !== true || hasOnlyLines) return; - var opacity = Array.isArray(marker.opacity) ? 1 : marker.opacity; - if(polygon === false) { // clear selection for(i = 0; i < cd.length; i++) cd[i].dim = 0; } @@ -46,7 +41,7 @@ module.exports = function selectPoints(searchInfo, polygon) { pointNumber: i, x: di.x, y: di.y, - //FIXME: di.id is undefined for scattergls + // FIXME: di.id is undefined for scattergls id: di.id }); di.dim = 0; @@ -55,36 +50,18 @@ module.exports = function selectPoints(searchInfo, polygon) { } } - // do the dimming here, as well as returning the selection - // The logic here duplicates Drawing.pointStyle, but I don't want - // d.dim in pointStyle in case something goes wrong with selection. -// cd[0].node3.selectAll('path.point') -// .style('opacity', function(d) { -// return ((d.mo + 1 || opacity + 1) - 1) * (d.dim ? DESELECTDIM : 1); -// }); -// cd[0].node3.selectAll('text') -// .style('opacity', function(d) { -// return d.dim ? DESELECTDIM : 1; -// }); - - - - //TODO: highlight selected points here + // highlight selected points here var traceObj = cd[0].plot; var scene = cd[0].plot.scene; var fullTrace = cd[0].trace; - //FIXME: remove - // fullTrace.x = fullTrace.x.slice(1) - // fullTrace.y = fullTrace.y.slice(1) - - fullTrace.selection = selection + fullTrace.selection = selection; // scene.plot([fullTrace], [cd], scene.fullLayout); // excerpt from ↑ - traceObj.update(fullTrace, cd) - scene.glplot.setDirty() + traceObj.update(fullTrace, cd); + scene.glplot.setDirty(); return selection; }; From fb84a06cbf96d637d2e8ae4932238b110549485b Mon Sep 17 00:00:00 2001 From: Dmitry Date: Thu, 25 May 2017 16:01:12 -0400 Subject: [PATCH 17/61] Resurrect hoveron --- src/plots/cartesian/graph_interact.js | 1 - src/traces/scattergl/attributes.js | 14 +++++++++++++- src/traces/scattergl/defaults.js | 11 +++++++++++ src/traces/scattergl/index.js | 1 + 4 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/plots/cartesian/graph_interact.js b/src/plots/cartesian/graph_interact.js index dec0b24c748..91c062d9c21 100644 --- a/src/plots/cartesian/graph_interact.js +++ b/src/plots/cartesian/graph_interact.js @@ -620,7 +620,6 @@ function hover(gd, evt, subplot) { container: fullLayout._hoverlayer, outerContainer: fullLayout._paperdiv }; - var hoverLabels = createHoverText(hoverData, labelOpts); hoverAvoidOverlaps(hoverData, rotateLabels ? 'xa' : 'ya'); diff --git a/src/traces/scattergl/attributes.js b/src/traces/scattergl/attributes.js index 2764714a5a7..8f8f0f65c5a 100644 --- a/src/traces/scattergl/attributes.js +++ b/src/traces/scattergl/attributes.js @@ -84,5 +84,17 @@ module.exports = { fillcolor: scatterAttrs.fillcolor, error_y: scatterAttrs.error_y, - error_x: scatterAttrs.error_x + error_x: scatterAttrs.error_x, + + hoveron: { + valType: 'flaglist', + flags: ['points', 'fills'], + role: 'info', + description: [ + 'Do the hover effects highlight individual points (markers or', + 'line points) or do they highlight filled regions?', + 'If the fill is *toself* or *tonext* and there are no markers', + 'or text, then the default is *fills*, otherwise it is *points*.' + ].join(' ') + } }; diff --git a/src/traces/scattergl/defaults.js b/src/traces/scattergl/defaults.js index 442363ae113..b1f05be5c14 100644 --- a/src/traces/scattergl/defaults.js +++ b/src/traces/scattergl/defaults.js @@ -50,6 +50,17 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout handleFillColorDefaults(traceIn, traceOut, defaultColor, coerce); } + var dfltHoverOn = []; + + if(subTypes.hasMarkers(traceOut) || subTypes.hasText(traceOut)) { + dfltHoverOn.push('points'); + } + if(traceOut.fill === 'tonext' || traceOut.fill === 'toself') { + dfltHoverOn.push('fills'); + } + + coerce('hoveron', dfltHoverOn.join('+') || 'points'); + errorBarsSupplyDefaults(traceIn, traceOut, defaultColor, {axis: 'y'}); errorBarsSupplyDefaults(traceIn, traceOut, defaultColor, {axis: 'x', inherit: 'y'}); }; diff --git a/src/traces/scattergl/index.js b/src/traces/scattergl/index.js index 8d0e1a40caf..0ed87fee261 100644 --- a/src/traces/scattergl/index.js +++ b/src/traces/scattergl/index.js @@ -13,6 +13,7 @@ var ScatterGl = {}; ScatterGl.attributes = require('./attributes'); ScatterGl.supplyDefaults = require('./defaults'); ScatterGl.colorbar = require('../scatter/colorbar'); +ScatterGl.hoverPoints = require('../scatter/hover'); // reuse the Scatter3D 'dummy' calc step so that legends know what to do ScatterGl.calc = require('../scatter/calc'); From 82c71eb75380b5dcfde7107438b318f68597c552 Mon Sep 17 00:00:00 2001 From: Dmitry Date: Thu, 25 May 2017 16:56:56 -0400 Subject: [PATCH 18/61] Normalize selected zoom behaviour --- src/plots/cartesian/index.js | 3 +++ src/plots/gl2d/index.js | 4 ++++ src/traces/scattergl/convert.js | 3 +++ 3 files changed, 10 insertions(+) diff --git a/src/plots/cartesian/index.js b/src/plots/cartesian/index.js index 367f41d2656..248448c96ca 100644 --- a/src/plots/cartesian/index.js +++ b/src/plots/cartesian/index.js @@ -184,6 +184,9 @@ exports.clean = function(newFullData, newFullLayout, oldFullData, oldFullLayout) oldFullLayout._infolayer.select('.' + axIds[i] + 'title').remove(); } } + + //clean selection + oldFullLayout._zoomlayer.selectAll('.select-outline').remove(); }; exports.drawFramework = function(gd) { diff --git a/src/plots/gl2d/index.js b/src/plots/gl2d/index.js index e6f491823c7..77a8c3a1e17 100644 --- a/src/plots/gl2d/index.js +++ b/src/plots/gl2d/index.js @@ -13,6 +13,7 @@ var Scene2D = require('./scene2d'); var Plots = require('../plots'); var xmlnsNamespaces = require('../../constants/xmlns_namespaces'); var constants = require('../cartesian/constants'); +var cartesian = require('../cartesian') exports.name = 'gl2d'; @@ -80,6 +81,9 @@ exports.clean = function(newFullData, newFullLayout, oldFullData, oldFullLayout) delete oldFullLayout._plots[id]; } } + + //since we use cartesian interactions, do cartesian clean + cartesian.clean.apply(this, arguments) }; exports.drawFramework = function(gd) { diff --git a/src/traces/scattergl/convert.js b/src/traces/scattergl/convert.js index 5d72121e01d..2dd64ee8835 100644 --- a/src/traces/scattergl/convert.js +++ b/src/traces/scattergl/convert.js @@ -285,6 +285,9 @@ proto.update = function(options) { } this.selectedIds = sel; } + else { + this.selectedIds = {} + } if(!this.isVisible) { this.line.clear(); From 70851467afb0ca8877ebe4eb0c7d780b582ab519 Mon Sep 17 00:00:00 2001 From: Dmitry Date: Thu, 25 May 2017 19:07:14 -0400 Subject: [PATCH 19/61] Fix losing trace reference --- src/plot_api/plot_api.js | 4 +++- src/plots/cartesian/dragbox.js | 1 - src/traces/scattergl/convert.js | 12 +++++++----- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/plot_api/plot_api.js b/src/plot_api/plot_api.js index 7a41fcfb0e4..e1d0ce2bc4d 100644 --- a/src/plot_api/plot_api.js +++ b/src/plot_api/plot_api.js @@ -1778,7 +1778,9 @@ Plotly.relayout = function relayout(gd, astr, val) { flags = specs.flags; // clear calcdata if required - if(flags.docalc) gd.calcdata = undefined; + if(flags.docalc) { + gd.calcdata = undefined; + } // fill in redraw sequence diff --git a/src/plots/cartesian/dragbox.js b/src/plots/cartesian/dragbox.js index 4add9ea13bb..587620c92ba 100644 --- a/src/plots/cartesian/dragbox.js +++ b/src/plots/cartesian/dragbox.js @@ -634,7 +634,6 @@ module.exports = function dragBox(gd, plotinfo, x, y, w, h, ns, ew) { } } } - gd.emit('plotly_doubleclick', null); Plotly.relayout(gd, attrs); } diff --git a/src/traces/scattergl/convert.js b/src/traces/scattergl/convert.js index 2dd64ee8835..69107ef7f22 100644 --- a/src/traces/scattergl/convert.js +++ b/src/traces/scattergl/convert.js @@ -253,7 +253,7 @@ function _convertColor(colors, opacities, count) { return result; } -proto.update = function(options) { +proto.update = function(options, cdscatter) { if(options.visible !== true) { this.isVisible = false; this.hasLines = false; @@ -318,6 +318,11 @@ proto.update = function(options) { // not quite on-par with 'scatter', but close enough for now // does not handle the colorscale case this.color = getTraceColor(options, {}); + + //provide reference for selecting points + if (cdscatter && cdscatter[0] && !cdscatter[0].plot) { + cdscatter[0].plot = this; + } }; // We'd ideally know that all values are of fast types; sampling gives no certainty but faster @@ -721,10 +726,7 @@ proto.dispose = function() { function createLineWithMarkers(scene, data, cdscatter) { var plot = new LineWithMarkers(scene, data.uid); - plot.update(data); - - // save for selecting points - cdscatter[0].plot = plot; + plot.update(data, cdscatter); return plot; } From 86d7d65b68f54a15dffb561603a317c8abda7e48 Mon Sep 17 00:00:00 2001 From: Dmitry Date: Fri, 26 May 2017 13:07:14 -0400 Subject: [PATCH 20/61] Fix fancy scatter --- src/plots/cartesian/axes.js | 3 +-- src/traces/scattergl/convert.js | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/plots/cartesian/axes.js b/src/plots/cartesian/axes.js index 5dc9cda7d66..2d7b0ca629e 100644 --- a/src/plots/cartesian/axes.js +++ b/src/plots/cartesian/axes.js @@ -365,8 +365,7 @@ axes.doAutoRange = function(ax) { var hasDeps = (ax._min && ax._max && ax._min.length && ax._max.length); if(ax.autorange && hasDeps) { - // FIXME: scattergl calls it in async fashion, hence there is .range and _._rl mess - ax.range = ax._rl = axes.getAutoRange(ax); + ax.range = axes.getAutoRange(ax); ax._r = ax.range.slice(); ax._rl = Lib.simpleMap(ax._r, ax.r2l); diff --git a/src/traces/scattergl/convert.js b/src/traces/scattergl/convert.js index 69107ef7f22..74665344b5c 100644 --- a/src/traces/scattergl/convert.js +++ b/src/traces/scattergl/convert.js @@ -286,7 +286,7 @@ proto.update = function(options, cdscatter) { this.selectedIds = sel; } else { - this.selectedIds = {} + this.selectedIds = null } if(!this.isVisible) { From d21176b96425c89f023be7e6eccf60af7da913f6 Mon Sep 17 00:00:00 2001 From: Dmitry Date: Fri, 26 May 2017 13:25:10 -0400 Subject: [PATCH 21/61] Capitalize cartesian --- src/plots/gl2d/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plots/gl2d/index.js b/src/plots/gl2d/index.js index 77a8c3a1e17..edffc89659f 100644 --- a/src/plots/gl2d/index.js +++ b/src/plots/gl2d/index.js @@ -13,7 +13,7 @@ var Scene2D = require('./scene2d'); var Plots = require('../plots'); var xmlnsNamespaces = require('../../constants/xmlns_namespaces'); var constants = require('../cartesian/constants'); -var cartesian = require('../cartesian') +var Cartesian = require('../cartesian') exports.name = 'gl2d'; @@ -83,7 +83,7 @@ exports.clean = function(newFullData, newFullLayout, oldFullData, oldFullLayout) } //since we use cartesian interactions, do cartesian clean - cartesian.clean.apply(this, arguments) + Cartesian.clean.apply(this, arguments) }; exports.drawFramework = function(gd) { From 8a1c8b8b43636544107753800a51b9625e8b8258 Mon Sep 17 00:00:00 2001 From: Dmitry Date: Fri, 26 May 2017 13:26:57 -0400 Subject: [PATCH 22/61] Recycle cartesian drawFramework --- src/plots/gl2d/index.js | 38 +------------------------------------- 1 file changed, 1 insertion(+), 37 deletions(-) diff --git a/src/plots/gl2d/index.js b/src/plots/gl2d/index.js index edffc89659f..31334366eff 100644 --- a/src/plots/gl2d/index.js +++ b/src/plots/gl2d/index.js @@ -86,43 +86,7 @@ exports.clean = function(newFullData, newFullLayout, oldFullData, oldFullLayout) Cartesian.clean.apply(this, arguments) }; -exports.drawFramework = function(gd) { - var fullLayout = gd._fullLayout, - subplotData = makeSubplotData(gd); - - var subplotLayers = fullLayout._cartesianlayer.selectAll('.subplot') - .data(subplotData, Lib.identity); - subplotLayers.enter().append('g') - .attr('class', function(name) { return 'subplot ' + name; }); - - subplotLayers.order(); - - subplotLayers.exit() - .call(purgeSubplotLayers, fullLayout); - - subplotLayers.each(function(name) { - var plotinfo = fullLayout._plots[name]; - - // keep ref to plot group - plotinfo.plotgroup = d3.select(this); - - // initialize list of overlay subplots - plotinfo.overlays = []; - - makeSubplotLayer(plotinfo); - - // fill in list of overlay subplots - if(plotinfo.mainplot) { - var mainplot = fullLayout._plots[plotinfo.mainplot]; - mainplot.overlays.push(plotinfo); - } - - // make separate drag layers for each subplot, - // but append them to paper rather than the plot groups, - // so they end up on top of the rest - plotinfo.draglayer = joinLayer(fullLayout._draggers, 'g', name); - }); -}; +exports.drawFramework = Cartesian.drawFramework exports.toSVG = function(gd) { var fullLayout = gd._fullLayout, From 82610d1b78443ccfcbf54ff584599ac7f0ad6d83 Mon Sep 17 00:00:00 2001 From: Dmitry Date: Fri, 26 May 2017 13:29:13 -0400 Subject: [PATCH 23/61] Remove cartesian remnants --- src/plots/gl2d/index.js | 98 ----------------------------------------- 1 file changed, 98 deletions(-) diff --git a/src/plots/gl2d/index.js b/src/plots/gl2d/index.js index 31334366eff..2f7137bc2bb 100644 --- a/src/plots/gl2d/index.js +++ b/src/plots/gl2d/index.js @@ -170,101 +170,3 @@ function makeSubplotData(gd) { return subplotData; } - -function makeSubplotLayer(plotinfo) { - var plotgroup = plotinfo.plotgroup, - id = plotinfo.id; - - // Layers to keep plot types in the right order. - // from back to front: - // 1. heatmaps, 2D histos and contour maps - // 2. bars / 1D histos - // 3. errorbars for bars and scatter - // 4. scatter - // 5. box plots - function joinPlotLayers(parent) { - joinLayer(parent, 'g', 'imagelayer'); - joinLayer(parent, 'g', 'maplayer'); - joinLayer(parent, 'g', 'barlayer'); - joinLayer(parent, 'g', 'carpetlayer'); - joinLayer(parent, 'g', 'boxlayer'); - joinLayer(parent, 'g', 'scatterlayer'); - } - - if(!plotinfo.mainplot) { - var backLayer = joinLayer(plotgroup, 'g', 'layer-subplot'); - plotinfo.shapelayer = joinLayer(backLayer, 'g', 'shapelayer'); - plotinfo.imagelayer = joinLayer(backLayer, 'g', 'imagelayer'); - - plotinfo.gridlayer = joinLayer(plotgroup, 'g', 'gridlayer'); - plotinfo.overgrid = joinLayer(plotgroup, 'g', 'overgrid'); - - plotinfo.zerolinelayer = joinLayer(plotgroup, 'g', 'zerolinelayer'); - plotinfo.overzero = joinLayer(plotgroup, 'g', 'overzero'); - - plotinfo.plot = joinLayer(plotgroup, 'g', 'plot'); - plotinfo.overplot = joinLayer(plotgroup, 'g', 'overplot'); - - plotinfo.xlines = joinLayer(plotgroup, 'path', 'xlines'); - plotinfo.ylines = joinLayer(plotgroup, 'path', 'ylines'); - plotinfo.overlines = joinLayer(plotgroup, 'g', 'overlines'); - - plotinfo.xaxislayer = joinLayer(plotgroup, 'g', 'xaxislayer'); - plotinfo.yaxislayer = joinLayer(plotgroup, 'g', 'yaxislayer'); - plotinfo.overaxes = joinLayer(plotgroup, 'g', 'overaxes'); - } - else { - var mainplotinfo = plotinfo.mainplotinfo; - - // now make the components of overlaid subplots - // overlays don't have backgrounds, and append all - // their other components to the corresponding - // extra groups of their main plots. - - plotinfo.gridlayer = joinLayer(mainplotinfo.overgrid, 'g', id); - plotinfo.zerolinelayer = joinLayer(mainplotinfo.overzero, 'g', id); - - plotinfo.plot = joinLayer(mainplotinfo.overplot, 'g', id); - plotinfo.xlines = joinLayer(mainplotinfo.overlines, 'path', id); - plotinfo.ylines = joinLayer(mainplotinfo.overlines, 'path', id); - plotinfo.xaxislayer = joinLayer(mainplotinfo.overaxes, 'g', id); - plotinfo.yaxislayer = joinLayer(mainplotinfo.overaxes, 'g', id); - } - - // common attributes for all subplots, overlays or not - plotinfo.plot.call(joinPlotLayers); - - plotinfo.xlines - .style('fill', 'none') - .classed('crisp', true); - - plotinfo.ylines - .style('fill', 'none') - .classed('crisp', true); -} - -function purgeSubplotLayers(layers, fullLayout) { - if(!layers) return; - - layers.each(function(subplot) { - var plotgroup = d3.select(this), - clipId = 'clip' + fullLayout._uid + subplot + 'plot'; - - plotgroup.remove(); - fullLayout._draggers.selectAll('g.' + subplot).remove(); - fullLayout._defs.select('#' + clipId).remove(); - - // do not remove individual axis s here - // as other subplots may need them - }); -} - -function joinLayer(parent, nodeType, className) { - var layer = parent.selectAll('.' + className) - .data([0]); - - layer.enter().append(nodeType) - .classed(className, true); - - return layer; -} From 74028ebe7267fa42ce8eb5e79887a17e419ee003 Mon Sep 17 00:00:00 2001 From: Dmitry Date: Fri, 26 May 2017 13:38:42 -0400 Subject: [PATCH 24/61] Rename glTrace --- src/traces/scattergl/convert.js | 4 ++-- src/traces/scattergl/select.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/traces/scattergl/convert.js b/src/traces/scattergl/convert.js index 74665344b5c..6e0c1d8b646 100644 --- a/src/traces/scattergl/convert.js +++ b/src/traces/scattergl/convert.js @@ -320,8 +320,8 @@ proto.update = function(options, cdscatter) { this.color = getTraceColor(options, {}); //provide reference for selecting points - if (cdscatter && cdscatter[0] && !cdscatter[0].plot) { - cdscatter[0].plot = this; + if (cdscatter && cdscatter[0] && !cdscatter[0].glTrace) { + cdscatter[0].glTrace = this; } }; diff --git a/src/traces/scattergl/select.js b/src/traces/scattergl/select.js index 48a11126879..91020a73fc5 100644 --- a/src/traces/scattergl/select.js +++ b/src/traces/scattergl/select.js @@ -51,8 +51,8 @@ module.exports = function selectPoints(searchInfo, polygon) { } // highlight selected points here - var traceObj = cd[0].plot; - var scene = cd[0].plot.scene; + var traceObj = cd[0].glTrace; + var scene = cd[0].glTrace.scene; var fullTrace = cd[0].trace; fullTrace.selection = selection; From e1da55ba111ee66e7a7620d5846e341367b74434 Mon Sep 17 00:00:00 2001 From: Dmitry Date: Fri, 26 May 2017 13:53:12 -0400 Subject: [PATCH 25/61] Remove extra check --- src/plots/cartesian/dragbox.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plots/cartesian/dragbox.js b/src/plots/cartesian/dragbox.js index ffed27add4f..ae07fc71193 100644 --- a/src/plots/cartesian/dragbox.js +++ b/src/plots/cartesian/dragbox.js @@ -79,7 +79,7 @@ module.exports = function dragBox(gd, plotinfo, x, y, w, h, ns, ew) { var yIDs = [ya0._id]; // if we're dragging two axes at once, also drag overlays - subplots = [plotinfo].concat((ns && ew && plotinfo.overlays) ? plotinfo.overlays : []); + subplots = [plotinfo].concat((ns && ew) ? plotinfo.overlays : []); for(var i = 1; i < subplots.length; i++) { var subplotXa = subplots[i].xaxis, From 4c0d822c503ff1fa1a0602179779a64ebe5318e5 Mon Sep 17 00:00:00 2001 From: Dmitry Date: Fri, 26 May 2017 14:00:38 -0400 Subject: [PATCH 26/61] Lintify --- src/plots/cartesian/index.js | 2 +- src/plots/gl2d/index.js | 70 ++------------------------------- src/traces/scattergl/convert.js | 6 +-- 3 files changed, 8 insertions(+), 70 deletions(-) diff --git a/src/plots/cartesian/index.js b/src/plots/cartesian/index.js index 248448c96ca..9338aa0f5b4 100644 --- a/src/plots/cartesian/index.js +++ b/src/plots/cartesian/index.js @@ -185,7 +185,7 @@ exports.clean = function(newFullData, newFullLayout, oldFullData, oldFullLayout) } } - //clean selection + // clean selection oldFullLayout._zoomlayer.selectAll('.select-outline').remove(); }; diff --git a/src/plots/gl2d/index.js b/src/plots/gl2d/index.js index 2f7137bc2bb..4c91f4fd535 100644 --- a/src/plots/gl2d/index.js +++ b/src/plots/gl2d/index.js @@ -13,7 +13,7 @@ var Scene2D = require('./scene2d'); var Plots = require('../plots'); var xmlnsNamespaces = require('../../constants/xmlns_namespaces'); var constants = require('../cartesian/constants'); -var Cartesian = require('../cartesian') +var Cartesian = require('../cartesian'); exports.name = 'gl2d'; @@ -25,10 +25,6 @@ exports.idRegex = constants.idRegex; exports.attrRegex = constants.attrRegex; -var axisIds = require('../cartesian/axis_ids'); -var Lib = require('../../lib'); -var d3 = require('d3'); - exports.attributes = require('../cartesian/attributes'); exports.plot = function plotGl2d(gd) { @@ -82,11 +78,11 @@ exports.clean = function(newFullData, newFullLayout, oldFullData, oldFullLayout) } } - //since we use cartesian interactions, do cartesian clean - Cartesian.clean.apply(this, arguments) + // since we use cartesian interactions, do cartesian clean + Cartesian.clean.apply(this, arguments); }; -exports.drawFramework = Cartesian.drawFramework +exports.drawFramework = Cartesian.drawFramework; exports.toSVG = function(gd) { var fullLayout = gd._fullLayout, @@ -112,61 +108,3 @@ exports.toSVG = function(gd) { scene.destroy(); } }; - - -function makeSubplotData(gd) { - var fullLayout = gd._fullLayout, - subplots = Object.keys(fullLayout._plots); - - var subplotData = [], - overlays = []; - - for(var i = 0; i < subplots.length; i++) { - var subplot = subplots[i], - plotinfo = fullLayout._plots[subplot]; - - var xa = plotinfo.xaxis, - ya = plotinfo.yaxis; - - // is this subplot overlaid on another? - // ax.overlaying is the id of another axis of the same - // dimension that this one overlays to be an overlaid subplot, - // the main plot must exist make sure we're not trying to - // overlay on an axis that's already overlaying another - var xa2 = axisIds.getFromId(gd, xa.overlaying) || xa; - if(xa2 !== xa && xa2.overlaying) { - xa2 = xa; - xa.overlaying = false; - } - - var ya2 = axisIds.getFromId(gd, ya.overlaying) || ya; - if(ya2 !== ya && ya2.overlaying) { - ya2 = ya; - ya.overlaying = false; - } - - var mainplot = xa2._id + ya2._id; - if(mainplot !== subplot && subplots.indexOf(mainplot) !== -1) { - plotinfo.mainplot = mainplot; - plotinfo.mainplotinfo = fullLayout._plots[mainplot]; - overlays.push(subplot); - - // for now force overlays to overlay completely... so they - // can drag together correctly and share backgrounds. - // Later perhaps we make separate axis domain and - // tick/line domain or something, so they can still share - // the (possibly larger) dragger and background but don't - // have to both be drawn over that whole domain - xa.domain = xa2.domain.slice(); - ya.domain = ya2.domain.slice(); - } - else { - subplotData.push(subplot); - } - } - - // main subplots before overlays - subplotData = subplotData.concat(overlays); - - return subplotData; -} diff --git a/src/traces/scattergl/convert.js b/src/traces/scattergl/convert.js index 6e0c1d8b646..a25e0f98f2b 100644 --- a/src/traces/scattergl/convert.js +++ b/src/traces/scattergl/convert.js @@ -286,7 +286,7 @@ proto.update = function(options, cdscatter) { this.selectedIds = sel; } else { - this.selectedIds = null + this.selectedIds = null; } if(!this.isVisible) { @@ -319,8 +319,8 @@ proto.update = function(options, cdscatter) { // does not handle the colorscale case this.color = getTraceColor(options, {}); - //provide reference for selecting points - if (cdscatter && cdscatter[0] && !cdscatter[0].glTrace) { + // provide reference for selecting points + if(cdscatter && cdscatter[0] && !cdscatter[0].glTrace) { cdscatter[0].glTrace = this; } }; From 5d35300e9000d6e94b3e337d1c8db43d1754a51d Mon Sep 17 00:00:00 2001 From: Dmitry Date: Fri, 26 May 2017 14:10:00 -0400 Subject: [PATCH 27/61] Resurrect gl hover --- src/plots/gl2d/scene2d.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/plots/gl2d/scene2d.js b/src/plots/gl2d/scene2d.js index 5754b263a60..cc1fe62beca 100644 --- a/src/plots/gl2d/scene2d.js +++ b/src/plots/gl2d/scene2d.js @@ -152,6 +152,7 @@ proto.makeFramework = function() { var container = this.container; container.appendChild(canvas); container.appendChild(svgContainer); + container.appendChild(mouseContainer); }; proto.toImage = function(format) { @@ -358,6 +359,7 @@ proto.destroy = function() { if(!this.staticPlot) this.container.removeChild(this.canvas); this.container.removeChild(this.svgContainer); + this.container.removeChild(this.mouseContainer); this.fullData = null; this.glplot = null; @@ -390,6 +392,12 @@ proto.plot = function(fullData, calcData, fullLayout) { (height - size.t) - (1 - domainY[1]) * size.h ]; + this.mouseContainer.style.width = size.w * (domainX[1] - domainX[0]) + 'px'; + this.mouseContainer.style.height = size.h * (domainY[1] - domainY[0]) + 'px'; + this.mouseContainer.height = size.h * (domainY[1] - domainY[0]); + this.mouseContainer.style.left = size.l + domainX[0] * size.w + 'px'; + this.mouseContainer.style.top = size.t + (1 - domainY[1]) * size.h + 'px'; + var bounds = this.bounds; bounds[0] = bounds[1] = Infinity; bounds[2] = bounds[3] = -Infinity; From 7a952b1e12e4ec7d8804b45259aac0cb1b4886a9 Mon Sep 17 00:00:00 2001 From: Dmitry Date: Fri, 26 May 2017 18:07:44 -0400 Subject: [PATCH 28/61] Outline the way to implement camera-based selection --- src/components/dragelement/index.js | 14 +++++----- src/components/modebar/buttons.js | 1 + src/plots/gl2d/camera.js | 40 ++++++++++++++++++++++------- 3 files changed, 39 insertions(+), 16 deletions(-) diff --git a/src/components/dragelement/index.js b/src/components/dragelement/index.js index a748a37310d..1494ed636e1 100644 --- a/src/components/dragelement/index.js +++ b/src/components/dragelement/index.js @@ -62,6 +62,13 @@ dragElement.init = function init(options) { if(!gd._mouseDownTime) gd._mouseDownTime = 0; + // enable call to options.setCursor(evt) + initialOnMouseMove = options.element.onmousemove; + if(options.setCursor) options.element.onmousemove = options.setCursor; + + options.element.onmousedown = onStart; + options.element.style.pointerEvents = 'all'; + function onStart(e) { // disable call to options.setCursor(evt) options.element.onmousemove = initialOnMouseMove; @@ -165,13 +172,6 @@ dragElement.init = function init(options) { return Lib.pauseEvent(e); } - - // enable call to options.setCursor(evt) - initialOnMouseMove = options.element.onmousemove; - if(options.setCursor) options.element.onmousemove = options.setCursor; - - options.element.onmousedown = onStart; - options.element.style.pointerEvents = 'all'; }; function coverSlip() { diff --git a/src/components/modebar/buttons.js b/src/components/modebar/buttons.js index 776a75df610..4fe30bf493e 100644 --- a/src/components/modebar/buttons.js +++ b/src/components/modebar/buttons.js @@ -249,6 +249,7 @@ function handleCartesian(gd, ev) { } aobj[astr] = val; + } Plotly.relayout(gd, aobj); diff --git a/src/plots/gl2d/camera.js b/src/plots/gl2d/camera.js index e50cdae6012..4906651e179 100644 --- a/src/plots/gl2d/camera.js +++ b/src/plots/gl2d/camera.js @@ -65,6 +65,8 @@ function createCamera(scene) { var MINDRAG = cartesianConstants.MINDRAG * plot.pixelRatio; var MINZOOM = cartesianConstants.MINZOOM * plot.pixelRatio; + var mode = scene.fullLayout.dragmode; + var dx, dy; x *= plot.pixelRatio; @@ -89,8 +91,10 @@ function createCamera(scene) { } } - switch(scene.fullLayout.dragmode) { + switch(mode) { case 'zoom': + case 'select': + case 'lasso': if(buttons) { var dataX = x / (viewBox[2] - viewBox[0]) * (dataBox[2] - dataBox[0]) + @@ -119,6 +123,16 @@ function createCamera(scene) { result.boxStart[1] !== result.boxEnd[1]) ) { result.boxEnabled = true; + if (mode === 'select' || mode === 'lasso') { + // prepSelect(e, x, y, { + // element: dragger, //+ + // gd: this.element, //+ + // plotinfo: null, //+ + // xaxes: null, //+ + // yaxes: null, //+ + // subplot: null //+? + // }, mode); + } } // constrain aspect ratio if the axes require it @@ -172,16 +186,22 @@ function createCamera(scene) { else if(result.boxEnabled) { dx = result.boxStart[0] !== result.boxEnd[0]; dy = result.boxStart[1] !== result.boxEnd[1]; + if(dx || dy) { - if(dx) { - updateRange(0, result.boxStart[0], result.boxEnd[0]); - scene.xaxis.autorange = false; + if(mode === 'select' || mode === 'lasso') { + console.log('end'); } - if(dy) { - updateRange(1, result.boxStart[1], result.boxEnd[1]); - scene.yaxis.autorange = false; + else { + if(dx) { + updateRange(0, result.boxStart[0], result.boxEnd[0]); + scene.xaxis.autorange = false; + } + if(dy) { + updateRange(1, result.boxStart[1], result.boxEnd[1]); + scene.yaxis.autorange = false; + } + scene.relayoutCallback(); } - scene.relayoutCallback(); } else { scene.glplot.setDirty(); @@ -244,7 +264,9 @@ function createCamera(scene) { var lastX = result.lastPos[0], lastY = result.lastPos[1]; - switch(scene.fullLayout.dragmode) { + var mode = scene.fullLayout.dragmode; + + switch(mode) { case 'zoom': break; From b3bc7cb5891534cd176a1173e4f5ffae837ceb65 Mon Sep 17 00:00:00 2001 From: Dmitry Date: Fri, 26 May 2017 18:11:31 -0400 Subject: [PATCH 29/61] Get back cartesian hover mode --- src/plots/gl2d/scene2d.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plots/gl2d/scene2d.js b/src/plots/gl2d/scene2d.js index cc1fe62beca..898d9d9d30a 100644 --- a/src/plots/gl2d/scene2d.js +++ b/src/plots/gl2d/scene2d.js @@ -152,7 +152,7 @@ proto.makeFramework = function() { var container = this.container; container.appendChild(canvas); container.appendChild(svgContainer); - container.appendChild(mouseContainer); + // container.appendChild(mouseContainer); }; proto.toImage = function(format) { From c2935298618b1bb35e4f203f3b148b9d9a43b065 Mon Sep 17 00:00:00 2001 From: Dmitry Date: Fri, 26 May 2017 18:54:16 -0400 Subject: [PATCH 30/61] Lintify --- src/plots/gl2d/camera.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plots/gl2d/camera.js b/src/plots/gl2d/camera.js index 4906651e179..f247c235e23 100644 --- a/src/plots/gl2d/camera.js +++ b/src/plots/gl2d/camera.js @@ -123,7 +123,7 @@ function createCamera(scene) { result.boxStart[1] !== result.boxEnd[1]) ) { result.boxEnabled = true; - if (mode === 'select' || mode === 'lasso') { + if(mode === 'select' || mode === 'lasso') { // prepSelect(e, x, y, { // element: dragger, //+ // gd: this.element, //+ @@ -189,7 +189,7 @@ function createCamera(scene) { if(dx || dy) { if(mode === 'select' || mode === 'lasso') { - console.log('end'); + // console.log('end'); } else { if(dx) { From 4e6bff157ecbe96146760b5ef5adae0182ae816c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Tue, 30 May 2017 16:27:47 -0400 Subject: [PATCH 31/61] comment out mouseContainer removal in scene2d toImage - just to get toImage working again --- src/plots/gl2d/scene2d.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plots/gl2d/scene2d.js b/src/plots/gl2d/scene2d.js index 898d9d9d30a..3d46e296342 100644 --- a/src/plots/gl2d/scene2d.js +++ b/src/plots/gl2d/scene2d.js @@ -359,7 +359,7 @@ proto.destroy = function() { if(!this.staticPlot) this.container.removeChild(this.canvas); this.container.removeChild(this.svgContainer); - this.container.removeChild(this.mouseContainer); +// this.container.removeChild(this.mouseContainer); this.fullData = null; this.glplot = null; From 5d07d9938fc7577452eee781e3145207576c310c Mon Sep 17 00:00:00 2001 From: Dmitry Date: Tue, 6 Jun 2017 01:38:02 -0400 Subject: [PATCH 32/61] Fix selection state --- package.json | 6 ++-- src/traces/scattergl/convert.js | 59 ++++++++++++++------------------- src/traces/scattergl/select.js | 21 +++++++----- 3 files changed, 39 insertions(+), 47 deletions(-) diff --git a/package.json b/package.json index 573ee88924f..3a5f80dbf72 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,7 @@ "3d-view": "^2.0.0", "@plotly/d3-sankey": "^0.5.0", "alpha-shape": "^1.0.0", - "color-rgba": "^1.0.4", + "color-rgba": "^1.1.0", "convex-hull": "^1.0.3", "country-regex": "^1.1.0", "d3": "^3.5.12", @@ -66,7 +66,7 @@ "fast-isnumeric": "^1.1.1", "gl-contour2d": "^1.1.2", "gl-error2d": "^1.2.1", - "gl-error3d": "^1.0.5", + "gl-error3d": "^1.0.6", "gl-heatmap2d": "^1.0.3", "gl-line2d": "^1.4.1", "gl-line3d": "^1.1.0", @@ -75,7 +75,7 @@ "gl-plot2d": "^1.2.0", "gl-plot3d": "^1.5.4", "gl-pointcloud2d": "^1.0.0", - "gl-scatter2d": "^1.2.2", + "gl-scatter2d": "^1.3.1", "gl-scatter2d-sdf": "^1.3.9", "gl-scatter3d": "^1.0.4", "gl-select-box": "^1.0.1", diff --git a/src/traces/scattergl/convert.js b/src/traces/scattergl/convert.js index a25e0f98f2b..84480b754b6 100644 --- a/src/traces/scattergl/convert.js +++ b/src/traces/scattergl/convert.js @@ -96,12 +96,16 @@ function LineWithMarkers(scene, uid) { size: 12, color: [0, 0, 0, 1], borderSize: 1, - borderColor: [0, 0, 0, 1] + borderColor: [0, 0, 0, 1], + snapPoints: true }; + var scatterOptions1 = Lib.extendFlat({}, scatterOptions0, {snapPoints: false}); - this.scatter = this.initObject(createScatter, scatterOptions0, 3); + this.scatter = this.initObject(function (plot, options) { + return createScatter(plot, options) + }, scatterOptions0, 3); this.fancyScatter = this.initObject(createFancyScatter, scatterOptions0, 4); - this.selectScatter = this.initObject(createScatter, scatterOptions0, 5); + this.selectScatter = this.initObject(createScatter, scatterOptions1, 5); } var proto = LineWithMarkers.prototype; @@ -111,6 +115,14 @@ proto.initObject = function(createFn, options, objIndex) { var glplot = _this.scene.glplot; var options0 = Lib.extendFlat({}, options); var obj = null; + var result = { + options: options, + update: update, + clear: clear, + dispose: dispose + } + + return result function update() { if(!obj) { @@ -128,13 +140,6 @@ proto.initObject = function(createFn, options, objIndex) { function dispose() { if(obj) obj.dispose(); } - - return { - options: options, - update: update, - clear: clear, - dispose: dispose - }; }; proto.handlePick = function(pickResult) { @@ -268,27 +273,12 @@ proto.update = function(options, cdscatter) { this.hasErrorY = options.error_y.visible === true; this.hasMarkers = subTypes.hasMarkers(options); } - this.textLabels = options.text; this.name = options.name; this.hoverinfo = options.hoverinfo; this.bounds = [Infinity, Infinity, -Infinity, -Infinity]; this.connectgaps = !!options.connectgaps; - // form selection cache - var sel, selection = options.selection; - if(selection) { - sel = {}; - for(var i = 0, l = selection.length; i < l; i++) { - var pt = selection[i]; - sel[pt.pointNumber] = pt; - } - this.selectedIds = sel; - } - else { - this.selectedIds = null; - } - if(!this.isVisible) { this.line.clear(); this.errorX.clear(); @@ -359,7 +349,6 @@ proto.updateFast = function(options) { pId = 0, ptr = 0, selection = options.selection, - sel = this.selectedIds, i, selPositions, l; var xx, yy; @@ -383,11 +372,8 @@ proto.updateFast = function(options) { xx = Lib.dateTime2ms(xx, xcalendar); } - // ignore selected points from positions - if(!sel || !sel[i]) { - positions[ptr++] = xx; - positions[ptr++] = yy; - } + positions[ptr++] = xx; + positions[ptr++] = yy; idToIndex[pId++] = i; @@ -424,11 +410,11 @@ proto.updateFast = function(options) { // if we have selPositions array - means we have to render all points transparent, and selected points opaque if(selPositions) { - this.selectScatter.options.positions = positions; + this.selectScatter.options.positions = selPositions; markerColor = str2RGBArray(options.marker.color); borderColor = str2RGBArray(options.marker.line.color); - opacity = (options.opacity) * (options.marker.opacity) * DESELECTDIM; + opacity = (options.opacity) * (options.marker.opacity); markerColor[3] *= opacity; this.selectScatter.options.color = markerColor; @@ -439,14 +425,15 @@ proto.updateFast = function(options) { markerSize = options.marker.size; this.selectScatter.options.size = markerSize; this.selectScatter.options.borderSize = options.marker.line.width; + this.selectScatter.update(); - this.scatter.options.positions = selPositions; + this.scatter.options.positions = null; markerColor = str2RGBArray(options.marker.color); borderColor = str2RGBArray(options.marker.line.color); - opacity = (options.opacity) * (options.marker.opacity); + opacity = (options.opacity) * (options.marker.opacity) * DESELECTDIM; markerColor[3] *= opacity; this.scatter.options.color = markerColor; @@ -459,6 +446,8 @@ proto.updateFast = function(options) { this.scatter.options.borderSize = options.marker.line.width; this.scatter.update(); + + this.scatter.options.positions = positions; } else { diff --git a/src/traces/scattergl/select.js b/src/traces/scattergl/select.js index 91020a73fc5..6d45efc2ebe 100644 --- a/src/traces/scattergl/select.js +++ b/src/traces/scattergl/select.js @@ -23,26 +23,33 @@ module.exports = function selectPoints(searchInfo, polygon) { x, y; + var scattergl = cd[0].glTrace; + var scene = cd[0].glTrace.scene; + // TODO: include lines? that would require per-segment line properties var hasOnlyLines = (!subtypes.hasMarkers(trace) && !subtypes.hasText(trace)); if(trace.visible !== true || hasOnlyLines) return; + // filter out points by visible scatter ones + // var scatter2d = scattergl.scatter.instance + if(polygon === false) { // clear selection for(i = 0; i < cd.length; i++) cd[i].dim = 0; } else { for(i = 0; i < cd.length; i++) { di = cd[i]; + //FIXME: this affects performance for 1e6 points x = xa.c2p(di.x); y = ya.c2p(di.y); if(polygon.contains([x, y])) { selection.push({ - curveNumber: curveNumber, - pointNumber: i, + // curveNumber: curveNumber, + // pointNumber: i, x: di.x, y: di.y, // FIXME: di.id is undefined for scattergls - id: di.id + // id: di.id }); di.dim = 0; } @@ -51,16 +58,12 @@ module.exports = function selectPoints(searchInfo, polygon) { } // highlight selected points here - var traceObj = cd[0].glTrace; - var scene = cd[0].glTrace.scene; - var fullTrace = cd[0].trace; - - fullTrace.selection = selection; + trace.selection = selection; // scene.plot([fullTrace], [cd], scene.fullLayout); // excerpt from ↑ - traceObj.update(fullTrace, cd); + scattergl.update(trace, cd); scene.glplot.setDirty(); return selection; From 6fb7c57b27888b13fc68785a0b6c3a2e7117d754 Mon Sep 17 00:00:00 2001 From: Dmitry Date: Tue, 6 Jun 2017 01:50:20 -0400 Subject: [PATCH 33/61] Rearrange scatter run --- src/traces/scattergl/convert.js | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/src/traces/scattergl/convert.js b/src/traces/scattergl/convert.js index 84480b754b6..c8ad5520366 100644 --- a/src/traces/scattergl/convert.js +++ b/src/traces/scattergl/convert.js @@ -9,7 +9,7 @@ 'use strict'; -var createScatter = require('gl-scatter2d'); +var createScatter = require('../../../../gl-scatter2d'); var createFancyScatter = require('gl-scatter2d-sdf'); var createLine = require('gl-line2d'); var createError = require('gl-error2d'); @@ -410,44 +410,42 @@ proto.updateFast = function(options) { // if we have selPositions array - means we have to render all points transparent, and selected points opaque if(selPositions) { - this.selectScatter.options.positions = selPositions; + this.scatter.options.positions = positions; markerColor = str2RGBArray(options.marker.color); borderColor = str2RGBArray(options.marker.line.color); - opacity = (options.opacity) * (options.marker.opacity); + opacity = (options.opacity) * (options.marker.opacity) * DESELECTDIM; markerColor[3] *= opacity; - this.selectScatter.options.color = markerColor; + this.scatter.options.color = markerColor; borderColor[3] *= opacity; - this.selectScatter.options.borderColor = borderColor; + this.scatter.options.borderColor = borderColor; markerSize = options.marker.size; - this.selectScatter.options.size = markerSize; - this.selectScatter.options.borderSize = options.marker.line.width; + this.scatter.options.size = markerSize; + this.scatter.options.borderSize = options.marker.line.width; - this.selectScatter.update(); + this.scatter.update(); - this.scatter.options.positions = null; + this.selectScatter.options.positions = selPositions; markerColor = str2RGBArray(options.marker.color); borderColor = str2RGBArray(options.marker.line.color); - opacity = (options.opacity) * (options.marker.opacity) * DESELECTDIM; + opacity = (options.opacity) * (options.marker.opacity); markerColor[3] *= opacity; - this.scatter.options.color = markerColor; + this.selectScatter.options.color = markerColor; borderColor[3] *= opacity; - this.scatter.options.borderColor = borderColor; + this.selectScatter.options.borderColor = borderColor; markerSize = options.marker.size; - this.scatter.options.size = markerSize; - this.scatter.options.borderSize = options.marker.line.width; - - this.scatter.update(); + this.selectScatter.options.size = markerSize; + this.selectScatter.options.borderSize = options.marker.line.width; - this.scatter.options.positions = positions; + this.selectScatter.update(); } else { @@ -456,7 +454,6 @@ proto.updateFast = function(options) { markerColor = str2RGBArray(options.marker.color); borderColor = str2RGBArray(options.marker.line.color); opacity = (options.opacity) * (options.marker.opacity); - markerColor[3] *= opacity; this.scatter.options.color = markerColor; From 3cb6ab1e5d3e0907df1d6776e13883921106e6aa Mon Sep 17 00:00:00 2001 From: Dmitry Date: Tue, 6 Jun 2017 12:42:32 -0400 Subject: [PATCH 34/61] Reduce calc time --- src/traces/scattergl/calc.js | 38 +++++++++++++++++++++++++++++++++++ src/traces/scattergl/index.js | 2 +- 2 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 src/traces/scattergl/calc.js diff --git a/src/traces/scattergl/calc.js b/src/traces/scattergl/calc.js new file mode 100644 index 00000000000..f232cbeee84 --- /dev/null +++ b/src/traces/scattergl/calc.js @@ -0,0 +1,38 @@ +/** +* Copyright 2012-2017, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + +var isNumeric = require('fast-isnumeric'); + +var Axes = require('../../plots/cartesian/axes'); + +var calcColorscale = require('../scatter/colorscale_calc'); +var arraysToCalcdata = require('../scatter/arrays_to_calcdata'); + + +module.exports = function calc(gd, trace) { + var xa = Axes.getFromId(gd, trace.xaxis || 'x'), + ya = Axes.getFromId(gd, trace.yaxis || 'y'); + + var x = xa.makeCalcdata(trace, 'x'), + y = ya.makeCalcdata(trace, 'y'); + + var serieslen = Math.min(x.length, y.length), i; + + // create the "calculated data" to plot + var cd = new Array(serieslen); + for(i = 0; i < serieslen; i++) { + cd[i] = {x: x[i], y: y[i]}; + } + + arraysToCalcdata(cd, trace); + + return cd; +}; diff --git a/src/traces/scattergl/index.js b/src/traces/scattergl/index.js index 0ed87fee261..551e9adad7d 100644 --- a/src/traces/scattergl/index.js +++ b/src/traces/scattergl/index.js @@ -16,7 +16,7 @@ ScatterGl.colorbar = require('../scatter/colorbar'); ScatterGl.hoverPoints = require('../scatter/hover'); // reuse the Scatter3D 'dummy' calc step so that legends know what to do -ScatterGl.calc = require('../scatter/calc'); +ScatterGl.calc = require('./calc'); ScatterGl.plot = require('./convert'); ScatterGl.selectPoints = require('./select'); From b0f0ed3ad56ba785b1e8861d3fcec184df92cef7 Mon Sep 17 00:00:00 2001 From: Dmitry Date: Tue, 6 Jun 2017 12:50:14 -0400 Subject: [PATCH 35/61] Reduce calc time more --- src/traces/scattergl/calc.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/traces/scattergl/calc.js b/src/traces/scattergl/calc.js index f232cbeee84..b3ae88676b3 100644 --- a/src/traces/scattergl/calc.js +++ b/src/traces/scattergl/calc.js @@ -32,7 +32,7 @@ module.exports = function calc(gd, trace) { cd[i] = {x: x[i], y: y[i]}; } - arraysToCalcdata(cd, trace); + arraysToCalcdata([{x: false, y: false, trace: trace, t: {}}], trace); return cd; }; From a74b301e9d5f8f32a3fcc386292ceb97584e385c Mon Sep 17 00:00:00 2001 From: Dmitry Date: Tue, 6 Jun 2017 13:37:00 -0400 Subject: [PATCH 36/61] Remove arrays to calcdata --- src/traces/scattergl/calc.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/traces/scattergl/calc.js b/src/traces/scattergl/calc.js index b3ae88676b3..1b6c9429288 100644 --- a/src/traces/scattergl/calc.js +++ b/src/traces/scattergl/calc.js @@ -32,7 +32,5 @@ module.exports = function calc(gd, trace) { cd[i] = {x: x[i], y: y[i]}; } - arraysToCalcdata([{x: false, y: false, trace: trace, t: {}}], trace); - return cd; }; From 73ab98d52e04407022f5050c6abc57f236ee235d Mon Sep 17 00:00:00 2001 From: Dmitry Date: Tue, 6 Jun 2017 13:53:14 -0400 Subject: [PATCH 37/61] Clean up --- src/traces/scattergl/calc.js | 6 ------ src/traces/scattergl/convert.js | 8 +++----- src/traces/scattergl/select.js | 13 ++++--------- 3 files changed, 7 insertions(+), 20 deletions(-) diff --git a/src/traces/scattergl/calc.js b/src/traces/scattergl/calc.js index 1b6c9429288..352b5acf00a 100644 --- a/src/traces/scattergl/calc.js +++ b/src/traces/scattergl/calc.js @@ -9,14 +9,8 @@ 'use strict'; -var isNumeric = require('fast-isnumeric'); - var Axes = require('../../plots/cartesian/axes'); -var calcColorscale = require('../scatter/colorscale_calc'); -var arraysToCalcdata = require('../scatter/arrays_to_calcdata'); - - module.exports = function calc(gd, trace) { var xa = Axes.getFromId(gd, trace.xaxis || 'x'), ya = Axes.getFromId(gd, trace.yaxis || 'y'); diff --git a/src/traces/scattergl/convert.js b/src/traces/scattergl/convert.js index c8ad5520366..0cf672cb1fa 100644 --- a/src/traces/scattergl/convert.js +++ b/src/traces/scattergl/convert.js @@ -101,9 +101,7 @@ function LineWithMarkers(scene, uid) { }; var scatterOptions1 = Lib.extendFlat({}, scatterOptions0, {snapPoints: false}); - this.scatter = this.initObject(function (plot, options) { - return createScatter(plot, options) - }, scatterOptions0, 3); + this.scatter = this.initObject(createScatter, scatterOptions0, 3); this.fancyScatter = this.initObject(createFancyScatter, scatterOptions0, 4); this.selectScatter = this.initObject(createScatter, scatterOptions1, 5); } @@ -120,9 +118,9 @@ proto.initObject = function(createFn, options, objIndex) { update: update, clear: clear, dispose: dispose - } + }; - return result + return result; function update() { if(!obj) { diff --git a/src/traces/scattergl/select.js b/src/traces/scattergl/select.js index 6d45efc2ebe..0e64cfb8bb3 100644 --- a/src/traces/scattergl/select.js +++ b/src/traces/scattergl/select.js @@ -17,7 +17,6 @@ module.exports = function selectPoints(searchInfo, polygon) { ya = searchInfo.yaxis, selection = [], trace = cd[0].trace, - curveNumber = trace.index, i, di, x, @@ -31,25 +30,21 @@ module.exports = function selectPoints(searchInfo, polygon) { if(trace.visible !== true || hasOnlyLines) return; // filter out points by visible scatter ones - // var scatter2d = scattergl.scatter.instance - - if(polygon === false) { // clear selection + if(polygon === false) { + // clear selection for(i = 0; i < cd.length; i++) cd[i].dim = 0; } else { for(i = 0; i < cd.length; i++) { di = cd[i]; - //FIXME: this affects performance for 1e6 points + // FIXME: this affects performance for 1e6 points x = xa.c2p(di.x); y = ya.c2p(di.y); if(polygon.contains([x, y])) { selection.push({ - // curveNumber: curveNumber, // pointNumber: i, x: di.x, - y: di.y, - // FIXME: di.id is undefined for scattergls - // id: di.id + y: di.y }); di.dim = 0; } From 8b8d2e3e3c95ecb680c1c3f1fa9f45480dcb7c04 Mon Sep 17 00:00:00 2001 From: Dmitry Date: Tue, 6 Jun 2017 14:22:02 -0400 Subject: [PATCH 38/61] Use proper gl-scatter2d dep --- src/traces/scattergl/convert.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/traces/scattergl/convert.js b/src/traces/scattergl/convert.js index 0cf672cb1fa..5db8d74b234 100644 --- a/src/traces/scattergl/convert.js +++ b/src/traces/scattergl/convert.js @@ -9,7 +9,7 @@ 'use strict'; -var createScatter = require('../../../../gl-scatter2d'); +var createScatter = require('gl-scatter2d'); var createFancyScatter = require('gl-scatter2d-sdf'); var createLine = require('gl-line2d'); var createError = require('gl-error2d'); From 14518022e94d8bba2f17f7fb896d5fd50c262242 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Tue, 6 Jun 2017 16:34:37 -0400 Subject: [PATCH 39/61] bring back scene2d mouseContainer + add updateFx to toggle dragmode - make mouseContainer transparent to pointer events on 'lasso' and 'select' dragmodes, this enables selection while keeping the current (and much faster) gl2d hover/click logic when dragmode is set to 'pan' or 'zoom' (the default). --- src/plot_api/subroutines.js | 6 ++++-- src/plots/gl2d/scene2d.js | 16 ++++++++++++++-- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/plot_api/subroutines.js b/src/plot_api/subroutines.js index 45238db4497..66c6ddb411f 100644 --- a/src/plot_api/subroutines.js +++ b/src/plot_api/subroutines.js @@ -386,8 +386,10 @@ exports.doModeBar = function(gd) { scene.updateFx(fullLayout.dragmode, fullLayout.hovermode); } - // no need to do this for gl2d subplots, - // Plots.linkSubplots takes care of it all. + subplotIds = Plots.getSubplotIds(fullLayout, 'gl2d'); + for(i = 0; i < subplotIds.length; i++) { + fullLayout._plots[subplotIds[i]]._scene2d.updateFx(); + } return Plots.previousPromises(gd); }; diff --git a/src/plots/gl2d/scene2d.js b/src/plots/gl2d/scene2d.js index 3d46e296342..b7be8a97457 100644 --- a/src/plots/gl2d/scene2d.js +++ b/src/plots/gl2d/scene2d.js @@ -147,12 +147,15 @@ proto.makeFramework = function() { // create div to catch the mouse event var mouseContainer = this.mouseContainer = document.createElement('div'); mouseContainer.style.position = 'absolute'; + mouseContainer.style['pointer-events'] = 'auto'; // append canvas, hover svg and mouse div to container var container = this.container; container.appendChild(canvas); container.appendChild(svgContainer); - // container.appendChild(mouseContainer); + container.appendChild(mouseContainer); + + this.updateFx(); }; proto.toImage = function(format) { @@ -359,7 +362,7 @@ proto.destroy = function() { if(!this.staticPlot) this.container.removeChild(this.canvas); this.container.removeChild(this.svgContainer); -// this.container.removeChild(this.mouseContainer); + this.container.removeChild(this.mouseContainer); this.fullData = null; this.glplot = null; @@ -507,7 +510,16 @@ proto.updateTraces = function(fullData, calcData) { this.glplot.objects.sort(function(a, b) { return a._trace.index - b._trace.index; }); +}; +proto.updateFx = function() { + var dragmode = this.fullLayout.dragmode; + + if(dragmode === 'lasso' || dragmode === 'select') { + this.mouseContainer.style['pointer-events'] = 'none'; + } else { + this.mouseContainer.style['pointer-events'] = 'auto'; + } }; proto.emitPointAction = function(nextSelection, eventType) { From de115d374e70b1e4e3d3af8a15934e96954052f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Tue, 6 Jun 2017 16:35:46 -0400 Subject: [PATCH 40/61] do not try to recurse into gl-vis object in Lib.minExtend - this led to max call stack errors previously on snapshots --- src/lib/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/index.js b/src/lib/index.js index 10b3226ac77..a42d5b1ff44 100644 --- a/src/lib/index.js +++ b/src/lib/index.js @@ -384,7 +384,7 @@ lib.minExtend = function(obj1, obj2) { for(i = 0; i < keys.length; i++) { k = keys[i]; v = obj1[k]; - if(k.charAt(0) === '_' || typeof v === 'function') continue; + if(k.charAt(0) === '_' || typeof v === 'function' || k === 'glTrace') continue; else if(k === 'module') objOut[k] = v; else if(Array.isArray(v)) objOut[k] = v.slice(0, arrayLen); else if(v && (typeof v === 'object')) objOut[k] = lib.minExtend(obj1[k], obj2[k]); From 3a30ae172d95a04ea16524be233ff52ca575b6de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Tue, 6 Jun 2017 16:36:24 -0400 Subject: [PATCH 41/61] don't try to remove selection elements when zoomlayer does not exist --- src/plots/cartesian/index.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/plots/cartesian/index.js b/src/plots/cartesian/index.js index 9338aa0f5b4..1415e69c7ec 100644 --- a/src/plots/cartesian/index.js +++ b/src/plots/cartesian/index.js @@ -186,7 +186,9 @@ exports.clean = function(newFullData, newFullLayout, oldFullData, oldFullLayout) } // clean selection - oldFullLayout._zoomlayer.selectAll('.select-outline').remove(); + if(oldFullLayout._zoomlayer) { + oldFullLayout._zoomlayer.selectAll('.select-outline').remove(); + } }; exports.drawFramework = function(gd) { From 1c61a47b1e97ea66b13c3175fa711b591a3d1136 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Tue, 6 Jun 2017 16:37:49 -0400 Subject: [PATCH 42/61] fixup 'scatter' -> addTraces 'scattergl' test - as now gl2d traces now come with cartesian svg layers (for select/lasso modes) --- test/jasmine/tests/gl_plot_interact_test.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/test/jasmine/tests/gl_plot_interact_test.js b/test/jasmine/tests/gl_plot_interact_test.js index a20a1dd0fb9..b68a2087e9f 100644 --- a/test/jasmine/tests/gl_plot_interact_test.js +++ b/test/jasmine/tests/gl_plot_interact_test.js @@ -936,7 +936,6 @@ describe('Test gl2d plots', function() { }); it('should clear orphan cartesian subplots on addTraces', function(done) { - Plotly.newPlot(gd, [], { xaxis: { title: 'X' }, yaxis: { title: 'Y' } @@ -949,12 +948,10 @@ describe('Test gl2d plots', function() { }]); }) .then(function() { - expect(d3.select('.subplot.xy').size()).toEqual(0); expect(d3.select('.xtitle').size()).toEqual(0); expect(d3.select('.ytitle').size()).toEqual(0); }) .then(done); - }); it('supports 1D and 2D Zoom', function(done) { From 9f57eff410242ef5ce7ae397554ae97723c69442 Mon Sep 17 00:00:00 2001 From: Dmitry Date: Tue, 6 Jun 2017 16:58:18 -0400 Subject: [PATCH 43/61] Ignore overcalculating snap-points --- src/traces/scattergl/convert.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/traces/scattergl/convert.js b/src/traces/scattergl/convert.js index 5db8d74b234..e88fa1b1eef 100644 --- a/src/traces/scattergl/convert.js +++ b/src/traces/scattergl/convert.js @@ -408,7 +408,7 @@ proto.updateFast = function(options) { // if we have selPositions array - means we have to render all points transparent, and selected points opaque if(selPositions) { - this.scatter.options.positions = positions; + this.scatter.options.positions = null; markerColor = str2RGBArray(options.marker.color); borderColor = str2RGBArray(options.marker.line.color); @@ -425,6 +425,7 @@ proto.updateFast = function(options) { this.scatter.options.borderSize = options.marker.line.width; this.scatter.update(); + this.scatter.options.positions = positions; this.selectScatter.options.positions = selPositions; From c60fb1d081c6878f72e83c59b90c0be093e7f30d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Tue, 6 Jun 2017 17:56:25 -0400 Subject: [PATCH 44/61] bring back scattergl marker colorscale --- src/traces/scattergl/calc.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/traces/scattergl/calc.js b/src/traces/scattergl/calc.js index 352b5acf00a..e04b58006fb 100644 --- a/src/traces/scattergl/calc.js +++ b/src/traces/scattergl/calc.js @@ -10,6 +10,7 @@ 'use strict'; var Axes = require('../../plots/cartesian/axes'); +var calcColorscales = require('../scatter/colorscale_calc'); module.exports = function calc(gd, trace) { var xa = Axes.getFromId(gd, trace.xaxis || 'x'), @@ -26,5 +27,7 @@ module.exports = function calc(gd, trace) { cd[i] = {x: x[i], y: y[i]}; } + calcColorscales(trace); + return cd; }; From 3921fddd05a1d918e4f16ed37542dc76a4b9337e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Tue, 6 Jun 2017 17:00:17 -0400 Subject: [PATCH 45/61] :hocho: hoveron in scattergl - hoveron was added *just* to make scatter/hover.js not break, it's functionality was never implemented in scattergl. --- src/traces/scatter/hover.js | 7 ++++--- src/traces/scattergl/attributes.js | 14 +------------- src/traces/scattergl/defaults.js | 11 ----------- 3 files changed, 5 insertions(+), 27 deletions(-) diff --git a/src/traces/scatter/hover.js b/src/traces/scatter/hover.js index 7ceac0db070..ee968926c64 100644 --- a/src/traces/scatter/hover.js +++ b/src/traces/scatter/hover.js @@ -24,11 +24,12 @@ module.exports = function hoverPoints(pointData, xval, yval, hovermode) { ya = pointData.ya, xpx = xa.c2p(xval), ypx = ya.c2p(yval), - pt = [xpx, ypx]; + pt = [xpx, ypx], + hoveron = trace.hoveron || ''; // look for points to hover on first, then take fills only if we // didn't find a point - if(trace.hoveron.indexOf('points') !== -1) { + if(hoveron.indexOf('points') !== -1) { var dx = function(di) { // scatter points: d.mrc is the calculated marker radius // adjust the distance so if you're inside the marker it @@ -84,7 +85,7 @@ module.exports = function hoverPoints(pointData, xval, yval, hovermode) { } // even if hoveron is 'fills', only use it if we have polygons too - if(trace.hoveron.indexOf('fills') !== -1 && trace._polygons) { + if(hoveron.indexOf('fills') !== -1 && trace._polygons) { var polygons = trace._polygons, polygonsIn = [], inside = false, diff --git a/src/traces/scattergl/attributes.js b/src/traces/scattergl/attributes.js index 8f8f0f65c5a..2764714a5a7 100644 --- a/src/traces/scattergl/attributes.js +++ b/src/traces/scattergl/attributes.js @@ -84,17 +84,5 @@ module.exports = { fillcolor: scatterAttrs.fillcolor, error_y: scatterAttrs.error_y, - error_x: scatterAttrs.error_x, - - hoveron: { - valType: 'flaglist', - flags: ['points', 'fills'], - role: 'info', - description: [ - 'Do the hover effects highlight individual points (markers or', - 'line points) or do they highlight filled regions?', - 'If the fill is *toself* or *tonext* and there are no markers', - 'or text, then the default is *fills*, otherwise it is *points*.' - ].join(' ') - } + error_x: scatterAttrs.error_x }; diff --git a/src/traces/scattergl/defaults.js b/src/traces/scattergl/defaults.js index b1f05be5c14..442363ae113 100644 --- a/src/traces/scattergl/defaults.js +++ b/src/traces/scattergl/defaults.js @@ -50,17 +50,6 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout handleFillColorDefaults(traceIn, traceOut, defaultColor, coerce); } - var dfltHoverOn = []; - - if(subTypes.hasMarkers(traceOut) || subTypes.hasText(traceOut)) { - dfltHoverOn.push('points'); - } - if(traceOut.fill === 'tonext' || traceOut.fill === 'toself') { - dfltHoverOn.push('fills'); - } - - coerce('hoveron', dfltHoverOn.join('+') || 'points'); - errorBarsSupplyDefaults(traceIn, traceOut, defaultColor, {axis: 'y'}); errorBarsSupplyDefaults(traceIn, traceOut, defaultColor, {axis: 'x', inherit: 'y'}); }; From 07b4c7c5787da1b7c7a97cf02c183d2e8038ec8e Mon Sep 17 00:00:00 2001 From: Dmitry Date: Wed, 7 Jun 2017 14:47:15 -0400 Subject: [PATCH 46/61] Fix scatter-fancy lasso --- src/traces/scattergl/convert.js | 13 ++++++++++--- src/traces/scattergl/select.js | 2 +- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/traces/scattergl/convert.js b/src/traces/scattergl/convert.js index e88fa1b1eef..24d1d494cdf 100644 --- a/src/traces/scattergl/convert.js +++ b/src/traces/scattergl/convert.js @@ -483,7 +483,7 @@ proto.updateFancy = function(options) { xaxis = scene.xaxis, yaxis = scene.yaxis, bounds = this.bounds, - sel = this.selectedIds; + selection = options.selection; // makeCalcdata runs d2c (data-to-coordinate) on every point var x = this.pickXData = xaxis.makeCalcdata(options, 'x').slice(); @@ -544,7 +544,14 @@ proto.updateFancy = function(options) { this.updateError('X', options, positions, errorsX); this.updateError('Y', options, positions, errorsY); - var sizes; + var sizes, selIds; + + if(selection) { + selIds = {}; + for(i = 0; i < selection.length; i++) { + selIds[selection[i].id] = true; + } + } if(this.hasMarkers) { this.scatter.options.positions = positions; @@ -579,7 +586,7 @@ proto.updateFancy = function(options) { for(j = 0; j < 4; ++j) { var color = colors[4 * index + j]; - if(sel && !sel[index] && j === 3) { + if(selIds && !selIds[index] && j === 3) { color *= DESELECTDIM; } this.scatter.options.colors[4 * i + j] = color; diff --git a/src/traces/scattergl/select.js b/src/traces/scattergl/select.js index 0e64cfb8bb3..0d8f2ef96c7 100644 --- a/src/traces/scattergl/select.js +++ b/src/traces/scattergl/select.js @@ -42,7 +42,7 @@ module.exports = function selectPoints(searchInfo, polygon) { y = ya.c2p(di.y); if(polygon.contains([x, y])) { selection.push({ - // pointNumber: i, + id: i, x: di.x, y: di.y }); From 6ac722d4d440bbff10568cecc46174076071f253 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Wed, 7 Jun 2017 14:49:41 -0400 Subject: [PATCH 47/61] fill scattergl calcdata only when dragmode is set to select/lasso - this brings back first render for 1e6 pts in close to 1500ms - add relayout logic so that updating into dragmode select/lasso triggers a recalc. --- src/plot_api/plot_api.js | 20 +++++++++++++++----- src/plot_api/subroutines.js | 5 ----- src/plots/gl2d/scene2d.js | 3 +-- src/traces/scattergl/calc.js | 28 +++++++++++++++++++--------- 4 files changed, 35 insertions(+), 21 deletions(-) diff --git a/src/plot_api/plot_api.js b/src/plot_api/plot_api.js index 5fe54dbe25e..a57e12cceb6 100644 --- a/src/plot_api/plot_api.js +++ b/src/plot_api/plot_api.js @@ -1939,7 +1939,8 @@ function _relayout(gd, aobj) { // trunk nodes (everything except the leaf) ptrunk = p.parts.slice(0, pend).join('.'), parentIn = Lib.nestedProperty(gd.layout, ptrunk).get(), - parentFull = Lib.nestedProperty(fullLayout, ptrunk).get(); + parentFull = Lib.nestedProperty(fullLayout, ptrunk).get(), + vOld = p.get(); if(vi === undefined) continue; @@ -1947,7 +1948,7 @@ function _relayout(gd, aobj) { // axis reverse is special - it is its own inverse // op and has no flag. - undoit[ai] = (pleaf === 'reverse') ? vi : p.get(); + undoit[ai] = (pleaf === 'reverse') ? vi : vOld; // Setting width or height to null must reset the graph's width / height // back to its initial value as computed during the first pass in Plots.plotAutoSize. @@ -2140,9 +2141,18 @@ function _relayout(gd, aobj) { ai.match(/^(bar|box|font)/)) { flags.docalc = true; } - else if(fullLayout._has('gl2d') && - (ai.indexOf('axis') !== -1 || ai === 'plot_bgcolor') - ) flags.doplot = true; + else if(fullLayout._has('gl2d')) { + if(ai.indexOf('axis') !== -1 || ai === 'plot_bgcolor') { + flags.doplot = true; + } + + if(ai === 'dragmode' && + (vi === 'lasso' || vi === 'select') && + !(vOld === 'lasso' || vOld === 'select') + ) { + flags.docalc = true; + } + } else if(ai === 'hiddenlabels') flags.docalc = true; else if(proot.indexOf('legend') !== -1) flags.dolegend = true; else if(ai.indexOf('title') !== -1) flags.doticks = true; diff --git a/src/plot_api/subroutines.js b/src/plot_api/subroutines.js index 66c6ddb411f..1dc6d9ed1a7 100644 --- a/src/plot_api/subroutines.js +++ b/src/plot_api/subroutines.js @@ -386,11 +386,6 @@ exports.doModeBar = function(gd) { scene.updateFx(fullLayout.dragmode, fullLayout.hovermode); } - subplotIds = Plots.getSubplotIds(fullLayout, 'gl2d'); - for(i = 0; i < subplotIds.length; i++) { - fullLayout._plots[subplotIds[i]]._scene2d.updateFx(); - } - return Plots.previousPromises(gd); }; diff --git a/src/plots/gl2d/scene2d.js b/src/plots/gl2d/scene2d.js index b7be8a97457..f148869db37 100644 --- a/src/plots/gl2d/scene2d.js +++ b/src/plots/gl2d/scene2d.js @@ -154,8 +154,6 @@ proto.makeFramework = function() { container.appendChild(canvas); container.appendChild(svgContainer); container.appendChild(mouseContainer); - - this.updateFx(); }; proto.toImage = function(format) { @@ -374,6 +372,7 @@ proto.plot = function(fullData, calcData, fullLayout) { this.updateRefs(fullLayout); this.updateTraces(fullData, calcData); + this.updateFx(); var width = fullLayout.width, height = fullLayout.height; diff --git a/src/traces/scattergl/calc.js b/src/traces/scattergl/calc.js index e04b58006fb..1be524af55e 100644 --- a/src/traces/scattergl/calc.js +++ b/src/traces/scattergl/calc.js @@ -10,21 +10,31 @@ 'use strict'; var Axes = require('../../plots/cartesian/axes'); +var arraysToCalcdata = require('../scatter/arrays_to_calcdata'); var calcColorscales = require('../scatter/colorscale_calc'); module.exports = function calc(gd, trace) { - var xa = Axes.getFromId(gd, trace.xaxis || 'x'), - ya = Axes.getFromId(gd, trace.yaxis || 'y'); + var dragmode = gd._fullLayout.dragmode; + var cd; - var x = xa.makeCalcdata(trace, 'x'), - y = ya.makeCalcdata(trace, 'y'); + if(dragmode === 'lasso' || dragmode === 'select') { + var xa = Axes.getFromId(gd, trace.xaxis || 'x'); + var ya = Axes.getFromId(gd, trace.yaxis || 'y'); - var serieslen = Math.min(x.length, y.length), i; + var x = xa.makeCalcdata(trace, 'x'); + var y = ya.makeCalcdata(trace, 'y'); - // create the "calculated data" to plot - var cd = new Array(serieslen); - for(i = 0; i < serieslen; i++) { - cd[i] = {x: x[i], y: y[i]}; + var serieslen = Math.min(x.length, y.length), i; + + // create the "calculated data" to plot + cd = new Array(serieslen); + + for(i = 0; i < serieslen; i++) { + cd[i] = {x: x[i], y: y[i]}; + } + } else { + cd = [{x: false, y: false, trace: trace, t: {}}]; + arraysToCalcdata(cd, trace); } calcColorscales(trace); From 2ceede1b9ad7126e9bd0f552c73a8d95b27e79e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Wed, 7 Jun 2017 16:54:20 -0400 Subject: [PATCH 48/61] don't try to style plot bg in gl2d case - ... to make image test pass --- src/plot_api/subroutines.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/plot_api/subroutines.js b/src/plot_api/subroutines.js index 1dc6d9ed1a7..747960f9d3e 100644 --- a/src/plot_api/subroutines.js +++ b/src/plot_api/subroutines.js @@ -126,15 +126,16 @@ exports.lsInner = function(gd) { var freefinished = []; subplotSelection.each(function(subplot) { - var plotinfo = fullLayout._plots[subplot], - xa = Plotly.Axes.getFromId(gd, subplot, 'x'), + var plotinfo = fullLayout._plots[subplot]; + + var xa = Plotly.Axes.getFromId(gd, subplot, 'x'), ya = Plotly.Axes.getFromId(gd, subplot, 'y'); // reset scale in case the margins have changed xa.setScale(); ya.setScale(); - if(plotinfo.bg) { + if(plotinfo.bg && fullLayout._has('cartesian')) { plotinfo.bg .call(Drawing.setRect, xa._offset - gs.p, ya._offset - gs.p, From 33dda89e4760d48415b23a9fa9e9d8440d69ca0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Wed, 7 Jun 2017 17:42:30 -0400 Subject: [PATCH 49/61] don't try to style grid lines either --- src/plot_api/subroutines.js | 44 +++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/src/plot_api/subroutines.js b/src/plot_api/subroutines.js index 747960f9d3e..973ed8c1414 100644 --- a/src/plot_api/subroutines.js +++ b/src/plot_api/subroutines.js @@ -255,27 +255,29 @@ exports.lsInner = function(gd) { rightpos += xa._offset - gs.l; } - plotinfo.xlines - .attr('transform', originx) - .attr('d', ( - (showbottom ? (xpathPrefix + bottompos + xpathSuffix) : '') + - (showtop ? (xpathPrefix + toppos + xpathSuffix) : '') + - (showfreex ? (xpathPrefix + freeposx + xpathSuffix) : '')) || - // so it doesn't barf with no lines shown - 'M0,0') - .style('stroke-width', xlw + 'px') - .call(Color.stroke, xa.showline ? - xa.linecolor : 'rgba(0,0,0,0)'); - plotinfo.ylines - .attr('transform', originy) - .attr('d', ( - (showleft ? ('M' + leftpos + ypathSuffix) : '') + - (showright ? ('M' + rightpos + ypathSuffix) : '') + - (showfreey ? ('M' + freeposy + ypathSuffix) : '')) || - 'M0,0') - .attr('stroke-width', ylw + 'px') - .call(Color.stroke, ya.showline ? - ya.linecolor : 'rgba(0,0,0,0)'); + if(fullLayout._has('cartesian')) { + plotinfo.xlines + .attr('transform', originx) + .attr('d', ( + (showbottom ? (xpathPrefix + bottompos + xpathSuffix) : '') + + (showtop ? (xpathPrefix + toppos + xpathSuffix) : '') + + (showfreex ? (xpathPrefix + freeposx + xpathSuffix) : '')) || + // so it doesn't barf with no lines shown + 'M0,0') + .style('stroke-width', xlw + 'px') + .call(Color.stroke, xa.showline ? + xa.linecolor : 'rgba(0,0,0,0)'); + plotinfo.ylines + .attr('transform', originy) + .attr('d', ( + (showleft ? ('M' + leftpos + ypathSuffix) : '') + + (showright ? ('M' + rightpos + ypathSuffix) : '') + + (showfreey ? ('M' + freeposy + ypathSuffix) : '')) || + 'M0,0') + .attr('stroke-width', ylw + 'px') + .call(Color.stroke, ya.showline ? + ya.linecolor : 'rgba(0,0,0,0)'); + } plotinfo.xaxislayer.attr('transform', originx); plotinfo.yaxislayer.attr('transform', originy); From baabafec28b76778703b37eea689fc2a58896026 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Wed, 7 Jun 2017 17:58:41 -0400 Subject: [PATCH 50/61] don't draw cartesian framework on top of gl2d subplots in staticPlot - there's no need --- src/plots/gl2d/index.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/plots/gl2d/index.js b/src/plots/gl2d/index.js index 4c91f4fd535..482d55fa4b1 100644 --- a/src/plots/gl2d/index.js +++ b/src/plots/gl2d/index.js @@ -82,7 +82,11 @@ exports.clean = function(newFullData, newFullLayout, oldFullData, oldFullLayout) Cartesian.clean.apply(this, arguments); }; -exports.drawFramework = Cartesian.drawFramework; +exports.drawFramework = function(gd) { + if(!gd._context.staticPlot) { + Cartesian.drawFramework(gd); + } +}; exports.toSVG = function(gd) { var fullLayout = gd._fullLayout, From 0b7a9242297019ee112f82bec37f23336e527bc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Fri, 9 Jun 2017 13:03:11 -0400 Subject: [PATCH 51/61] fixup gl2d dramode logic (:lock: it with tests) --- src/plot_api/plot_api.js | 22 ++++----- test/jasmine/tests/plot_api_test.js | 70 +++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+), 11 deletions(-) diff --git a/src/plot_api/plot_api.js b/src/plot_api/plot_api.js index a57e12cceb6..46de105460d 100644 --- a/src/plot_api/plot_api.js +++ b/src/plot_api/plot_api.js @@ -2141,17 +2141,17 @@ function _relayout(gd, aobj) { ai.match(/^(bar|box|font)/)) { flags.docalc = true; } - else if(fullLayout._has('gl2d')) { - if(ai.indexOf('axis') !== -1 || ai === 'plot_bgcolor') { - flags.doplot = true; - } - - if(ai === 'dragmode' && - (vi === 'lasso' || vi === 'select') && - !(vOld === 'lasso' || vOld === 'select') - ) { - flags.docalc = true; - } + else if(fullLayout._has('gl2d') && + (ai.indexOf('axis') !== -1 || ai === 'plot_bgcolor') + ) { + flags.doplot = true; + } + else if(fullLayout._has('gl2d') && + (ai === 'dragmode' && + (vi === 'lasso' || vi === 'select') && + !(vOld === 'lasso' || vOld === 'select')) + ) { + flags.docalc = true; } else if(ai === 'hiddenlabels') flags.docalc = true; else if(proot.indexOf('legend') !== -1) flags.dolegend = true; diff --git a/test/jasmine/tests/plot_api_test.js b/test/jasmine/tests/plot_api_test.js index 24337538273..091db5983a9 100644 --- a/test/jasmine/tests/plot_api_test.js +++ b/test/jasmine/tests/plot_api_test.js @@ -254,6 +254,76 @@ describe('Test plot api', function() { }); }); + describe('Plotly.relayout subroutines switchboard', function() { + var mockedMethods = [ + 'layoutReplot', + 'doLegend', + 'layoutStyles', + 'doTicksRelayout', + 'doModeBar', + 'doCamera' + ]; + + beforeAll(function() { + mockedMethods.forEach(function(m) { + spyOn(subroutines, m); + }); + }); + + function mock(gd) { + mockedMethods.forEach(function(m) { + subroutines[m].calls.reset(); + }); + + Plots.supplyDefaults(gd); + Plots.doCalcdata(gd); + return gd; + } + + it('should trigger recalc when switching into select or lasso dragmode', function() { + var gd = mock({ + data: [{ + type: 'scattergl', + x: [1, 2, 3], + y: [1, 2, 3] + }], + layout: { + dragmode: 'zoom' + } + }); + + function expectModeBarOnly() { + expect(gd.calcdata).toBeDefined(); + expect(subroutines.doModeBar).toHaveBeenCalled(); + expect(subroutines.layoutReplot).not.toHaveBeenCalled(); + } + + function expectRecalc() { + expect(gd.calcdata).toBeUndefined(); + expect(subroutines.doModeBar).not.toHaveBeenCalled(); + expect(subroutines.layoutReplot).toHaveBeenCalled(); + } + + Plotly.relayout(gd, 'dragmode', 'pan'); + expectModeBarOnly(); + + Plotly.relayout(mock(gd), 'dragmode', 'lasso'); + expectRecalc(); + + Plotly.relayout(mock(gd), 'dragmode', 'select'); + expectModeBarOnly(); + + Plotly.relayout(mock(gd), 'dragmode', 'lasso'); + expectModeBarOnly(); + + Plotly.relayout(mock(gd), 'dragmode', 'zoom'); + expectModeBarOnly(); + + Plotly.relayout(mock(gd), 'dragmode', 'select'); + expectRecalc(); + }); + }); + describe('Plotly.restyle subroutines switchboard', function() { beforeEach(function() { spyOn(PlotlyInternal, 'plot'); From d76d64dd4324443e16519368dd3eeb4adc79b68e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Fri, 9 Jun 2017 15:10:59 -0400 Subject: [PATCH 52/61] call updateFx on doModeBar restyle calls --- src/plot_api/subroutines.js | 10 ++++++++-- src/plots/gl2d/scene2d.js | 6 ++---- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/plot_api/subroutines.js b/src/plot_api/subroutines.js index 973ed8c1414..64d1663910d 100644 --- a/src/plot_api/subroutines.js +++ b/src/plot_api/subroutines.js @@ -378,17 +378,23 @@ exports.doTicksRelayout = function(gd) { exports.doModeBar = function(gd) { var fullLayout = gd._fullLayout; - var subplotIds, i; + var subplotIds, scene, i; ModeBar.manage(gd); initInteractions(gd); subplotIds = Plots.getSubplotIds(fullLayout, 'gl3d'); for(i = 0; i < subplotIds.length; i++) { - var scene = fullLayout[subplotIds[i]]._scene; + scene = fullLayout[subplotIds[i]]._scene; scene.updateFx(fullLayout.dragmode, fullLayout.hovermode); } + subplotIds = Plots.getSubplotIds(fullLayout, 'gl2d'); + for(i = 0; i < subplotIds.length; i++) { + scene = fullLayout._plots[subplotIds[i]]._scene2d; + scene.updateFx(fullLayout.dragmode); + } + return Plots.previousPromises(gd); }; diff --git a/src/plots/gl2d/scene2d.js b/src/plots/gl2d/scene2d.js index f148869db37..1c59206b239 100644 --- a/src/plots/gl2d/scene2d.js +++ b/src/plots/gl2d/scene2d.js @@ -372,7 +372,7 @@ proto.plot = function(fullData, calcData, fullLayout) { this.updateRefs(fullLayout); this.updateTraces(fullData, calcData); - this.updateFx(); + this.updateFx(fullLayout.dragmode); var width = fullLayout.width, height = fullLayout.height; @@ -511,9 +511,7 @@ proto.updateTraces = function(fullData, calcData) { }); }; -proto.updateFx = function() { - var dragmode = this.fullLayout.dragmode; - +proto.updateFx = function(dragmode) { if(dragmode === 'lasso' || dragmode === 'select') { this.mouseContainer.style['pointer-events'] = 'none'; } else { From 58b36543ea4a553fb4c02aebbc9ec041663b1d7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Fri, 9 Jun 2017 14:21:32 -0400 Subject: [PATCH 53/61] rm useless PR diffs --- src/components/dragelement/index.js | 14 +++++----- src/components/modebar/buttons.js | 1 - src/components/modebar/manage.js | 1 + src/plot_api/plot_api.js | 5 ++-- src/plots/cartesian/dragbox.js | 11 ++------ src/plots/cartesian/graph_interact.js | 2 -- src/plots/cartesian/index.js | 1 + src/plots/cartesian/scale_zoom.js | 1 + src/plots/gl2d/camera.js | 40 ++++++--------------------- src/plots/gl2d/scene2d.js | 3 +- src/traces/scattergl/convert.js | 17 ++++++------ src/traces/scattergl/select.js | 5 ---- 12 files changed, 34 insertions(+), 67 deletions(-) diff --git a/src/components/dragelement/index.js b/src/components/dragelement/index.js index 1494ed636e1..a748a37310d 100644 --- a/src/components/dragelement/index.js +++ b/src/components/dragelement/index.js @@ -62,13 +62,6 @@ dragElement.init = function init(options) { if(!gd._mouseDownTime) gd._mouseDownTime = 0; - // enable call to options.setCursor(evt) - initialOnMouseMove = options.element.onmousemove; - if(options.setCursor) options.element.onmousemove = options.setCursor; - - options.element.onmousedown = onStart; - options.element.style.pointerEvents = 'all'; - function onStart(e) { // disable call to options.setCursor(evt) options.element.onmousemove = initialOnMouseMove; @@ -172,6 +165,13 @@ dragElement.init = function init(options) { return Lib.pauseEvent(e); } + + // enable call to options.setCursor(evt) + initialOnMouseMove = options.element.onmousemove; + if(options.setCursor) options.element.onmousemove = options.setCursor; + + options.element.onmousedown = onStart; + options.element.style.pointerEvents = 'all'; }; function coverSlip() { diff --git a/src/components/modebar/buttons.js b/src/components/modebar/buttons.js index 4fe30bf493e..776a75df610 100644 --- a/src/components/modebar/buttons.js +++ b/src/components/modebar/buttons.js @@ -249,7 +249,6 @@ function handleCartesian(gd, ev) { } aobj[astr] = val; - } Plotly.relayout(gd, aobj); diff --git a/src/components/modebar/manage.js b/src/components/modebar/manage.js index c34f3deb73a..a97a86eb37d 100644 --- a/src/components/modebar/manage.js +++ b/src/components/modebar/manage.js @@ -72,6 +72,7 @@ module.exports = function manageModeBar(gd) { function getButtonGroups(gd, buttonsToRemove, buttonsToAdd) { var fullLayout = gd._fullLayout, fullData = gd._fullData; + var hasCartesian = fullLayout._has('cartesian'), hasGL3D = fullLayout._has('gl3d'), hasGeo = fullLayout._has('geo'), diff --git a/src/plot_api/plot_api.js b/src/plot_api/plot_api.js index 46de105460d..ad658f5c9d7 100644 --- a/src/plot_api/plot_api.js +++ b/src/plot_api/plot_api.js @@ -187,6 +187,7 @@ Plotly.plot = function(gd, data, layout, config) { basePlotModules[i].drawFramework(gd); } } + return Lib.syncOrAsync([ subroutines.layoutStyles, drawAxes, @@ -1789,9 +1790,7 @@ Plotly.relayout = function relayout(gd, astr, val) { flags = specs.flags; // clear calcdata if required - if(flags.docalc) { - gd.calcdata = undefined; - } + if(flags.docalc) gd.calcdata = undefined; // fill in redraw sequence diff --git a/src/plots/cartesian/dragbox.js b/src/plots/cartesian/dragbox.js index ae07fc71193..43940066346 100644 --- a/src/plots/cartesian/dragbox.js +++ b/src/plots/cartesian/dragbox.js @@ -170,6 +170,7 @@ module.exports = function dragBox(gd, plotinfo, x, y, w, h, ns, ew) { } } }; + dragElement.init(dragOptions); var x0, @@ -386,9 +387,7 @@ module.exports = function dragBox(gd, plotinfo, x, y, w, h, ns, ew) { var axRange = Lib.simpleMap(ax.range, ax.r2l), v0 = axRange[0] + (axRange[1] - axRange[0]) * centerFraction; function doZoom(v) { return ax.l2r(v0 + (v - v0) * zoom); } - ax.range = axRange.map(doZoom); - } if(ew || isSubplotConstrained) { @@ -444,6 +443,7 @@ module.exports = function dragBox(gd, plotinfo, x, y, w, h, ns, ew) { } recomputeAxisLists(); + if(xActive === 'ew' || yActive === 'ns') { if(xActive) dragAxList(xa, dx); if(yActive) dragAxList(ya, dy); @@ -504,7 +504,6 @@ module.exports = function dragBox(gd, plotinfo, x, y, w, h, ns, ew) { // scale the other axis the same about its middle for(i = 0; i < xa.length; i++) { xa[i].range = xa[i]._r.slice(); - scaleZoom(xa[i], 1 - dy / ph); } dx = dy * pw / ph; @@ -513,7 +512,6 @@ module.exports = function dragBox(gd, plotinfo, x, y, w, h, ns, ew) { if(!yActive && xActive.length === 1) { for(i = 0; i < ya.length; i++) { ya[i].range = ya[i]._r.slice(); - scaleZoom(ya[i], 1 - dx / pw); } dy = dx * ph / pw; @@ -633,6 +631,7 @@ module.exports = function dragBox(gd, plotinfo, x, y, w, h, ns, ew) { } } } + gd.emit('plotly_doubleclick', null); Plotly.relayout(gd, attrs); } @@ -655,7 +654,6 @@ module.exports = function dragBox(gd, plotinfo, x, y, w, h, ns, ew) { if(axi._r[1] !== axi.range[1]) attrs[axi._name + '.range[1]'] = axi.range[1]; axi.range = axi._input.range = axi._r.slice(); - } updateSubplots([0, 0, pw, ph]); @@ -815,12 +813,10 @@ function zoomAxRanges(axList, r0Fraction, r1Fraction, linkedAxes) { axRangeLinear0 = axi._rl[0]; axRangeLinearSpan = axi._rl[1] - axRangeLinear0; - axi.range = [ axi.l2r(axRangeLinear0 + axRangeLinearSpan * r0Fraction), axi.l2r(axRangeLinear0 + axRangeLinearSpan * r1Fraction) ]; - } // zoom linked axes about their centers @@ -835,7 +831,6 @@ function dragAxList(axList, pix) { for(var i = 0; i < axList.length; i++) { var axi = axList[i]; if(!axi.fixedrange) { - axi.range = [ axi.l2r(axi._rl[0] - pix / axi._m), axi.l2r(axi._rl[1] - pix / axi._m) diff --git a/src/plots/cartesian/graph_interact.js b/src/plots/cartesian/graph_interact.js index 0d6f964d7f3..283309865f5 100644 --- a/src/plots/cartesian/graph_interact.js +++ b/src/plots/cartesian/graph_interact.js @@ -38,8 +38,6 @@ module.exports = function initInteractions(gd) { subplots.forEach(function(subplot) { var plotinfo = fullLayout._plots[subplot]; - // if(!fullLayout._has('cartesian')) return; - var xa = plotinfo.xaxis, ya = plotinfo.yaxis, diff --git a/src/plots/cartesian/index.js b/src/plots/cartesian/index.js index 1415e69c7ec..883acb4d0d8 100644 --- a/src/plots/cartesian/index.js +++ b/src/plots/cartesian/index.js @@ -286,6 +286,7 @@ function makeSubplotData(gd) { subplotData.push(subplot); } } + // main subplots before overlays subplotData = subplotData.concat(overlays); diff --git a/src/plots/cartesian/scale_zoom.js b/src/plots/cartesian/scale_zoom.js index 90562164b61..7669f742301 100644 --- a/src/plots/cartesian/scale_zoom.js +++ b/src/plots/cartesian/scale_zoom.js @@ -15,6 +15,7 @@ module.exports = function scaleZoom(ax, factor, centerFraction) { var rangeLinear = [ax.r2l(ax.range[0]), ax.r2l(ax.range[1])]; var center = rangeLinear[0] + (rangeLinear[1] - rangeLinear[0]) * centerFraction; var newHalfSpan = (center - rangeLinear[0]) * factor; + ax.range = ax._input.range = [ ax.l2r(center - newHalfSpan), ax.l2r(center + newHalfSpan) diff --git a/src/plots/gl2d/camera.js b/src/plots/gl2d/camera.js index f247c235e23..e50cdae6012 100644 --- a/src/plots/gl2d/camera.js +++ b/src/plots/gl2d/camera.js @@ -65,8 +65,6 @@ function createCamera(scene) { var MINDRAG = cartesianConstants.MINDRAG * plot.pixelRatio; var MINZOOM = cartesianConstants.MINZOOM * plot.pixelRatio; - var mode = scene.fullLayout.dragmode; - var dx, dy; x *= plot.pixelRatio; @@ -91,10 +89,8 @@ function createCamera(scene) { } } - switch(mode) { + switch(scene.fullLayout.dragmode) { case 'zoom': - case 'select': - case 'lasso': if(buttons) { var dataX = x / (viewBox[2] - viewBox[0]) * (dataBox[2] - dataBox[0]) + @@ -123,16 +119,6 @@ function createCamera(scene) { result.boxStart[1] !== result.boxEnd[1]) ) { result.boxEnabled = true; - if(mode === 'select' || mode === 'lasso') { - // prepSelect(e, x, y, { - // element: dragger, //+ - // gd: this.element, //+ - // plotinfo: null, //+ - // xaxes: null, //+ - // yaxes: null, //+ - // subplot: null //+? - // }, mode); - } } // constrain aspect ratio if the axes require it @@ -186,22 +172,16 @@ function createCamera(scene) { else if(result.boxEnabled) { dx = result.boxStart[0] !== result.boxEnd[0]; dy = result.boxStart[1] !== result.boxEnd[1]; - if(dx || dy) { - if(mode === 'select' || mode === 'lasso') { - // console.log('end'); + if(dx) { + updateRange(0, result.boxStart[0], result.boxEnd[0]); + scene.xaxis.autorange = false; } - else { - if(dx) { - updateRange(0, result.boxStart[0], result.boxEnd[0]); - scene.xaxis.autorange = false; - } - if(dy) { - updateRange(1, result.boxStart[1], result.boxEnd[1]); - scene.yaxis.autorange = false; - } - scene.relayoutCallback(); + if(dy) { + updateRange(1, result.boxStart[1], result.boxEnd[1]); + scene.yaxis.autorange = false; } + scene.relayoutCallback(); } else { scene.glplot.setDirty(); @@ -264,9 +244,7 @@ function createCamera(scene) { var lastX = result.lastPos[0], lastY = result.lastPos[1]; - var mode = scene.fullLayout.dragmode; - - switch(mode) { + switch(scene.fullLayout.dragmode) { case 'zoom': break; diff --git a/src/plots/gl2d/scene2d.js b/src/plots/gl2d/scene2d.js index 1c59206b239..da706ffb562 100644 --- a/src/plots/gl2d/scene2d.js +++ b/src/plots/gl2d/scene2d.js @@ -325,6 +325,7 @@ proto.cameraChanged = function() { var nextTicks = this.computeTickMarks(); var curTicks = this.glplotOptions.ticks; + if(compareTicks(nextTicks, curTicks)) { this.glplotOptions.ticks = nextTicks; this.glplotOptions.dataBox = camera.dataBox; @@ -424,6 +425,7 @@ proto.plot = function(fullData, calcData, fullLayout) { ax = this[AXES[i]]; ax._length = options.viewBox[i + 2] - options.viewBox[i]; + Axes.doAutoRange(ax); ax.setScale(); } @@ -440,7 +442,6 @@ proto.plot = function(fullData, calcData, fullLayout) { options.dataBox = this.calcDataBox(); options.merge(fullLayout); - glplot.update(options); // force redraw so that promise is returned when rendering is completed diff --git a/src/traces/scattergl/convert.js b/src/traces/scattergl/convert.js index 24d1d494cdf..2278c590ced 100644 --- a/src/traces/scattergl/convert.js +++ b/src/traces/scattergl/convert.js @@ -113,14 +113,6 @@ proto.initObject = function(createFn, options, objIndex) { var glplot = _this.scene.glplot; var options0 = Lib.extendFlat({}, options); var obj = null; - var result = { - options: options, - update: update, - clear: clear, - dispose: dispose - }; - - return result; function update() { if(!obj) { @@ -138,6 +130,13 @@ proto.initObject = function(createFn, options, objIndex) { function dispose() { if(obj) obj.dispose(); } + + return { + options: options, + update: update, + clear: clear, + dispose: dispose + }; }; proto.handlePick = function(pickResult) { @@ -271,6 +270,7 @@ proto.update = function(options, cdscatter) { this.hasErrorY = options.error_y.visible === true; this.hasMarkers = subTypes.hasMarkers(options); } + this.textLabels = options.text; this.name = options.name; this.hoverinfo = options.hoverinfo; @@ -375,7 +375,6 @@ proto.updateFast = function(options) { idToIndex[pId++] = i; - bounds[0] = Math.min(bounds[0], xx); bounds[1] = Math.min(bounds[1], yy); bounds[2] = Math.max(bounds[2], xx); diff --git a/src/traces/scattergl/select.js b/src/traces/scattergl/select.js index 0d8f2ef96c7..1b123cedd6d 100644 --- a/src/traces/scattergl/select.js +++ b/src/traces/scattergl/select.js @@ -25,7 +25,6 @@ module.exports = function selectPoints(searchInfo, polygon) { var scattergl = cd[0].glTrace; var scene = cd[0].glTrace.scene; - // TODO: include lines? that would require per-segment line properties var hasOnlyLines = (!subtypes.hasMarkers(trace) && !subtypes.hasText(trace)); if(trace.visible !== true || hasOnlyLines) return; @@ -37,7 +36,6 @@ module.exports = function selectPoints(searchInfo, polygon) { else { for(i = 0; i < cd.length; i++) { di = cd[i]; - // FIXME: this affects performance for 1e6 points x = xa.c2p(di.x); y = ya.c2p(di.y); if(polygon.contains([x, y])) { @@ -55,9 +53,6 @@ module.exports = function selectPoints(searchInfo, polygon) { // highlight selected points here trace.selection = selection; - // scene.plot([fullTrace], [cd], scene.fullLayout); - - // excerpt from ↑ scattergl.update(trace, cd); scene.glplot.setDirty(); From 6bfbd8386a0880cb3269f570068d6eb3b115bda9 Mon Sep 17 00:00:00 2001 From: Dmitry Date: Mon, 12 Jun 2017 15:47:27 -0400 Subject: [PATCH 54/61] Ignore resnapping for scatter-fancy --- src/traces/scattergl/convert.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/traces/scattergl/convert.js b/src/traces/scattergl/convert.js index 2278c590ced..bd110d075da 100644 --- a/src/traces/scattergl/convert.js +++ b/src/traces/scattergl/convert.js @@ -593,7 +593,15 @@ proto.updateFancy = function(options) { } } - this.fancyScatter.update(); + //prevent scatter from resnapping points + if (selIds) { + this.scatter.options.positions = null + this.fancyScatter.update(); + this.scatter.options.positions = positions + } + else { + this.fancyScatter.update(); + } } else { this.fancyScatter.clear(); From 6c08dc3bd50984edd93dad0bfc91541c7bf431bf Mon Sep 17 00:00:00 2001 From: Dmitry Date: Mon, 12 Jun 2017 16:06:08 -0400 Subject: [PATCH 55/61] Lintify --- src/traces/scattergl/convert.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/traces/scattergl/convert.js b/src/traces/scattergl/convert.js index bd110d075da..1f469ee830b 100644 --- a/src/traces/scattergl/convert.js +++ b/src/traces/scattergl/convert.js @@ -593,11 +593,11 @@ proto.updateFancy = function(options) { } } - //prevent scatter from resnapping points - if (selIds) { - this.scatter.options.positions = null + // prevent scatter from resnapping points + if(selIds) { + this.scatter.options.positions = null; this.fancyScatter.update(); - this.scatter.options.positions = positions + this.scatter.options.positions = positions; } else { this.fancyScatter.update(); From b92059ac8b8b9acead1c38f9e7e5cfcad5138153 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Wed, 14 Jun 2017 17:06:54 -0400 Subject: [PATCH 56/61] add isDimmed option to fillColor util --- src/traces/scattergl/convert.js | 30 ++++++++++++------------------ 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/src/traces/scattergl/convert.js b/src/traces/scattergl/convert.js index 949c87f913b..5aa32134c9a 100644 --- a/src/traces/scattergl/convert.js +++ b/src/traces/scattergl/convert.js @@ -30,7 +30,7 @@ var DASHES = require('../../constants/gl2d_dashes'); var AXES = ['xaxis', 'yaxis']; var DESELECTDIM = 0.2; -var transparent = [0, 0, 0, 0]; +var TRANSPARENT = [0, 0, 0, 0]; function LineWithMarkers(scene, uid) { this.scene = scene; @@ -259,10 +259,14 @@ function isSymbolOpen(symbol) { return symbol.split('-open')[1] === ''; } -function fillColor(colorIn, colorOut, offsetIn, offsetOut) { - for(var j = 0; j < 4; j++) { +function fillColor(colorIn, colorOut, offsetIn, offsetOut, isDimmed) { + var dim = isDimmed ? DESELECTDIM : 1; + var j; + + for(j = 0; j < 3; j++) { colorIn[4 * offsetIn + j] = colorOut[4 * offsetOut + j]; } + colorIn[4 * offsetIn + j] = dim * colorOut[4 * offsetOut + j]; } proto.update = function(options, cdscatter) { @@ -582,7 +586,7 @@ proto.updateFancy = function(options) { var colors = convertColorScale(markerOpts, markerOpacity, traceOpacity, len); var borderWidths = convertNumber(markerOpts.line.width, len); var borderColors = convertColorScale(markerOpts.line, markerOpacity, traceOpacity, len); - var index, size, symbol, symbolSpec, isOpen, _colors, _borderColors, bw, minBorderWidth; + var index, size, symbol, symbolSpec, isOpen, isDimmed, _colors, _borderColors, bw, minBorderWidth; sizes = convertArray(markerSizeFunc, markerOpts.size, len); @@ -592,6 +596,7 @@ proto.updateFancy = function(options) { symbol = symbols[index]; symbolSpec = MARKER_SYMBOLS[symbol]; isOpen = isSymbolOpen(symbol); + isDimmed = selIds && !selIds[index]; if(symbolSpec.noBorder && !isOpen) { _colors = borderColors; @@ -615,23 +620,12 @@ proto.updateFancy = function(options) { this.scatter.options.glyphs[i] = symbolSpec.unicode; this.scatter.options.borderWidths[i] = 0.5 * ((bw > minBorderWidth) ? bw - minBorderWidth : 0); - // FIXME - - for(j = 0; j < 4; ++j) { - var color = colors[4 * index + j]; - if(selIds && !selIds[index] && j === 3) { - color *= DESELECTDIM; - } - this.scatter.options.colors[4 * i + j] = color; - this.scatter.options.borderColors[4 * i + j] = borderColors[4 * index + j]; - } - if(isOpen && !symbolSpec.noBorder && !symbolSpec.noFill) { - fillColor(this.scatter.options.colors, transparent, i, 0); + fillColor(this.scatter.options.colors, TRANSPARENT, i, 0); } else { - fillColor(this.scatter.options.colors, _colors, i, index); + fillColor(this.scatter.options.colors, _colors, i, index, isDimmed); } - fillColor(this.scatter.options.borderColors, _borderColors, i, index); + fillColor(this.scatter.options.borderColors, _borderColors, i, index, isDimmed); } // prevent scatter from resnapping points From d4aad3fd8a9b44c6fb89ba36b73f41d053303bae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Wed, 14 Jun 2017 18:17:27 -0400 Subject: [PATCH 57/61] add gl2d select/lasso tests --- test/jasmine/tests/gl2d_click_test.js | 146 ++++++++++++++++++++++++++ 1 file changed, 146 insertions(+) diff --git a/test/jasmine/tests/gl2d_click_test.js b/test/jasmine/tests/gl2d_click_test.js index 739055b81eb..42d34b7ad9b 100644 --- a/test/jasmine/tests/gl2d_click_test.js +++ b/test/jasmine/tests/gl2d_click_test.js @@ -444,3 +444,149 @@ describe('Test hover and click interactions', function() { .then(done); }); }); + +describe('Test gl2d lasso/select:', function() { + var mockFancy = require('@mocks/gl2d_14.json'); + var mockFast = Lib.extendDeep({}, mockFancy, { + data: [{mode: 'markers'}], + layout: { + xaxis: {type: 'linear'}, + yaxis: {type: 'linear'} + } + }); + + var gd; + var selectPath = [[93, 193], [143, 193]]; + var lassoPath = [[316, 171], [318, 239], [335, 243], [328, 169]]; + var lassoPath2 = [[93, 193], [143, 193], [143, 500], [93, 500], [93, 193]]; + + afterEach(function() { + Plotly.purge(gd); + destroyGraphDiv(); + }); + + function drag(path) { + var len = path.length; + + mouseEvent('mousemove', path[0][0], path[0][1]); + mouseEvent('mousedown', path[0][0], path[0][1]); + + path.slice(1, len).forEach(function(pt) { + mouseEvent('mousemove', pt[0], pt[1]); + }); + + mouseEvent('mouseup', path[len - 1][0], path[len - 1][1]); + } + + function select(path) { + return new Promise(function(resolve, reject) { + gd.once('plotly_selected', resolve); + setTimeout(function() { reject('did not trigger *plotly_selected*');}, 100); + drag(path); + }); + } + + function assertEventData(actual, expected) { + expect(actual.points.length).toBe(expected.points.length); + + expected.points.forEach(function(e, i) { + var a = actual.points[i]; + if(a) { + expect(a.x).toBe(e.x, 'x'); + expect(a.y).toBe(e.y, 'y'); + } + }); + } + + function countGlObjects() { + return gd._fullLayout._plots.xy._scene2d.glplot.objects.length; + } + + it('should work under fast mode with *select* dragmode', function(done) { + var _mock = Lib.extendDeep({}, mockFast); + _mock.layout.dragmode = 'select'; + gd = createGraphDiv(); + + Plotly.plot(gd, _mock).then(function() { + expect(countGlObjects()).toBe(1, 'has on gl-scatter2d object'); + + return select(selectPath); + }) + .then(function(eventData) { + assertEventData(eventData, { + points: [ + {x: 3.911, y: 0.401}, + {x: 5.34, y: 0.403}, + {x: 6.915, y: 0.411} + ] + }); + expect(countGlObjects()).toBe(2, 'adds a dimmed gl-scatter2d objects'); + }) + .catch(fail) + .then(done); + }); + + it('should work under fast mode with *lasso* dragmode', function(done) { + var _mock = Lib.extendDeep({}, mockFast); + _mock.layout.dragmode = 'lasso'; + gd = createGraphDiv(); + + Plotly.plot(gd, _mock).then(function() { + expect(countGlObjects()).toBe(1); + + return select(lassoPath2); + }) + .then(function(eventData) { + assertEventData(eventData, { + points: [ + {x: 3.911, y: 0.401}, + {x: 5.34, y: 0.403}, + {x: 6.915, y: 0.411} + ] + }); + expect(countGlObjects()).toBe(2); + }) + .catch(fail) + .then(done); + }); + + it('should work under fancy mode with *select* dragmode', function(done) { + var _mock = Lib.extendDeep({}, mockFancy); + _mock.layout.dragmode = 'select'; + gd = createGraphDiv(); + + Plotly.plot(gd, _mock).then(function() { + expect(countGlObjects()).toBe(2, 'has a gl-line2d and a gl-scatter2d-sdf'); + + return select(selectPath); + }) + .then(function(eventData) { + assertEventData(eventData, { + points: [{x: 0.004, y: 12.5}] + }); + expect(countGlObjects()).toBe(2, 'only changes colors of gl-scatter2d-sdf object'); + }) + .catch(fail) + .then(done); + }); + + it('should work under fancy mode with *lasso* dragmode', function(done) { + var _mock = Lib.extendDeep({}, mockFancy); + _mock.layout.dragmode = 'lasso'; + gd = createGraphDiv(); + + Plotly.plot(gd, _mock).then(function() { + expect(countGlObjects()).toBe(2, 'has a gl-line2d and a gl-scatter2d-sdf'); + + return select(lassoPath); + }) + .then(function(eventData) { + assertEventData(eventData, { + points: [{ x: 0.099, y: 2.75 }] + }); + expect(countGlObjects()).toBe(2, 'only changes colors of gl-scatter2d-sdf object'); + }) + .catch(fail) + .then(done); + }); +}); From c7b551c8e5d9010cc41a47bb0ff02c0ebf45204f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Wed, 14 Jun 2017 18:32:02 -0400 Subject: [PATCH 58/61] try adding delay after first Plotly.plot before gl2d select/lasso tests --- test/jasmine/tests/gl2d_click_test.js | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/test/jasmine/tests/gl2d_click_test.js b/test/jasmine/tests/gl2d_click_test.js index 42d34b7ad9b..188ef7c3b02 100644 --- a/test/jasmine/tests/gl2d_click_test.js +++ b/test/jasmine/tests/gl2d_click_test.js @@ -507,7 +507,9 @@ describe('Test gl2d lasso/select:', function() { _mock.layout.dragmode = 'select'; gd = createGraphDiv(); - Plotly.plot(gd, _mock).then(function() { + Plotly.plot(gd, _mock) + .then(delay(100)) + .then(function() { expect(countGlObjects()).toBe(1, 'has on gl-scatter2d object'); return select(selectPath); @@ -531,7 +533,9 @@ describe('Test gl2d lasso/select:', function() { _mock.layout.dragmode = 'lasso'; gd = createGraphDiv(); - Plotly.plot(gd, _mock).then(function() { + Plotly.plot(gd, _mock) + .then(delay(100)) + .then(function() { expect(countGlObjects()).toBe(1); return select(lassoPath2); @@ -555,7 +559,9 @@ describe('Test gl2d lasso/select:', function() { _mock.layout.dragmode = 'select'; gd = createGraphDiv(); - Plotly.plot(gd, _mock).then(function() { + Plotly.plot(gd, _mock) + .then(delay(100)) + .then(function() { expect(countGlObjects()).toBe(2, 'has a gl-line2d and a gl-scatter2d-sdf'); return select(selectPath); @@ -575,7 +581,9 @@ describe('Test gl2d lasso/select:', function() { _mock.layout.dragmode = 'lasso'; gd = createGraphDiv(); - Plotly.plot(gd, _mock).then(function() { + Plotly.plot(gd, _mock) + .then(delay(100)) + .then(function() { expect(countGlObjects()).toBe(2, 'has a gl-line2d and a gl-scatter2d-sdf'); return select(lassoPath); From 27811d62e0308864f5df072f58028d147e2c66f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Thu, 15 Jun 2017 10:09:51 -0400 Subject: [PATCH 59/61] bump gl-scatter2d-sdf to ^1.3.11 - to include: https://github.com/gl-vis/gl-scatter2d-sdf/commit/ae37fb66d6c0c23a7557b9508d2ac8c1f7971829 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6258961cc37..845f792c8a7 100644 --- a/package.json +++ b/package.json @@ -77,7 +77,7 @@ "gl-plot3d": "^1.5.4", "gl-pointcloud2d": "^1.0.0", "gl-scatter2d": "^1.3.1", - "gl-scatter2d-sdf": "^1.3.10", + "gl-scatter2d-sdf": "^1.3.11", "gl-scatter3d": "^1.0.4", "gl-select-box": "^1.0.1", "gl-shader": "4.2.0", From debdd4645eb77fa901f3305567e1f8170b89b700 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Thu, 15 Jun 2017 10:26:26 -0400 Subject: [PATCH 60/61] swap 'id' for 'pointNumber' in scattergl selection data - to match svg 'scatter' --- src/traces/scattergl/convert.js | 2 +- src/traces/scattergl/select.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/traces/scattergl/convert.js b/src/traces/scattergl/convert.js index 5aa32134c9a..74a2a1d9ea1 100644 --- a/src/traces/scattergl/convert.js +++ b/src/traces/scattergl/convert.js @@ -562,7 +562,7 @@ proto.updateFancy = function(options) { if(selection) { selIds = {}; for(i = 0; i < selection.length; i++) { - selIds[selection[i].id] = true; + selIds[selection[i].pointNumber] = true; } } diff --git a/src/traces/scattergl/select.js b/src/traces/scattergl/select.js index 1b123cedd6d..548824ad740 100644 --- a/src/traces/scattergl/select.js +++ b/src/traces/scattergl/select.js @@ -40,7 +40,7 @@ module.exports = function selectPoints(searchInfo, polygon) { y = ya.c2p(di.y); if(polygon.contains([x, y])) { selection.push({ - id: i, + pointNumber: i, x: di.x, y: di.y }); From 9d5d41179ddb6cabc7765b333fd938291f5f40f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Thu, 15 Jun 2017 18:29:22 -0400 Subject: [PATCH 61/61] skip gl2d lasso/select for now --- src/plots/cartesian/dragbox.js | 2 -- test/jasmine/tests/gl2d_click_test.js | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/plots/cartesian/dragbox.js b/src/plots/cartesian/dragbox.js index 46cdd041e31..603e88d2f78 100644 --- a/src/plots/cartesian/dragbox.js +++ b/src/plots/cartesian/dragbox.js @@ -696,9 +696,7 @@ module.exports = function dragBox(gd, plotinfo, x, y, w, h, ns, ew) { function scaleAndGetShift(ax, scaleFactor) { if(scaleFactor) { - ax.range = ax._r.slice(); - scaleZoom(ax, scaleFactor); return getShift(ax, scaleFactor); } diff --git a/test/jasmine/tests/gl2d_click_test.js b/test/jasmine/tests/gl2d_click_test.js index da1295fef03..ddd576d5ab4 100644 --- a/test/jasmine/tests/gl2d_click_test.js +++ b/test/jasmine/tests/gl2d_click_test.js @@ -445,7 +445,7 @@ describe('Test hover and click interactions', function() { }); }); -describe('Test gl2d lasso/select:', function() { +describe('@noCI Test gl2d lasso/select:', function() { var mockFancy = require('@mocks/gl2d_14.json'); var mockFast = Lib.extendDeep({}, mockFancy, { data: [{mode: 'markers'}],