From e1604d44012a54deea17eb5822d2653e12fd67fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikl=C3=B3s=20Tusz?= Date: Tue, 19 Jan 2016 16:18:14 -0500 Subject: [PATCH 1/9] Add svg image layer for heatmaps --- src/plot_api/plot_api.js | 1 + src/traces/heatmap/plot.js | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/plot_api/plot_api.js b/src/plot_api/plot_api.js index 86d92cdfea4..f08f16a7257 100644 --- a/src/plot_api/plot_api.js +++ b/src/plot_api/plot_api.js @@ -2657,6 +2657,7 @@ function makeCartesianPlotFramwork(gd, subplots) { // 4. scatter // 5. box plots function plotLayers(svg) { + svg.append('g').classed('imagelayer', true); svg.append('g').classed('maplayer', true); svg.append('g').classed('barlayer', true); svg.append('g').classed('errorlayer', true); diff --git a/src/traces/heatmap/plot.js b/src/traces/heatmap/plot.js index e087f306e18..ae61cbbaf28 100644 --- a/src/traces/heatmap/plot.js +++ b/src/traces/heatmap/plot.js @@ -368,7 +368,7 @@ function plotOne(gd, plotinfo, cd) { // put this right before making the new image, to minimize flicker fullLayout._paper.selectAll('.'+id).remove(); - plotinfo.plot.select('.maplayer').append('svg:image') + plotinfo.plot.select('.imagelayer').append('svg:image') .classed(id, true) .datum(cd[0]) .attr({ From a08332d4c522256ae80b4969b8b2a538346a4487 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Wed, 27 Jan 2016 13:58:31 -0500 Subject: [PATCH 2/9] expose d3 in test dashboard window scope --- devtools/test_dashboard/index.html | 2 ++ 1 file changed, 2 insertions(+) diff --git a/devtools/test_dashboard/index.html b/devtools/test_dashboard/index.html index d024ee9c6a0..5103afbd22b 100644 --- a/devtools/test_dashboard/index.html +++ b/devtools/test_dashboard/index.html @@ -36,6 +36,8 @@ return graphDiv; } }; + + var d3 = Plotly.d3; From b38a1cf041f976cbda567a099f1c4dae8846336c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Wed, 27 Jan 2016 14:04:27 -0500 Subject: [PATCH 3/9] select colorbar group from infolayer --- src/plot_api/plot_api.js | 10 ++++++++-- src/traces/contour/plot.js | 3 ++- src/traces/heatmap/plot.js | 8 ++++---- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/plot_api/plot_api.js b/src/plot_api/plot_api.js index a732f54154c..2ff8e403f44 100644 --- a/src/plot_api/plot_api.js +++ b/src/plot_api/plot_api.js @@ -297,8 +297,14 @@ Plotly.plot = function(gd, data, layout, config) { trace = cd[0].trace; if (trace.visible !== true || !trace._module.colorbar) { uid = trace.uid; - fullLayout._paper.selectAll('.hm'+uid+',.contour'+uid+',.cb'+uid+',#clip'+uid) - .remove(); + + fullLayout._paper.selectAll( + '.hm' + uid + + ',.contour' + uid + + ',#clip' + uid + ).remove(); + + fullLayout._infolayer.selectAll('.cb' + uid).remove(); } } diff --git a/src/traces/contour/plot.js b/src/traces/contour/plot.js index 4511320b51b..f0ed8be4b14 100644 --- a/src/traces/contour/plot.js +++ b/src/traces/contour/plot.js @@ -64,7 +64,8 @@ function plotOne(gd, plotinfo, cd) { pathinfo = emptyPathinfo(contours, plotinfo, cd[0]); if(trace.visible !== true) { - fullLayout._paper.selectAll('.'+id+',.cb'+uid+',.hm'+uid).remove(); + fullLayout._paper.selectAll('.' + id + ',.hm' + id).remove(); + fullLayout._infolayer.selectAll('.cb' + uid).remove(); return; } diff --git a/src/traces/heatmap/plot.js b/src/traces/heatmap/plot.js index d8ebe8c5d07..77de1ed3c65 100644 --- a/src/traces/heatmap/plot.js +++ b/src/traces/heatmap/plot.js @@ -33,14 +33,14 @@ function plotOne(gd, plotinfo, cd) { xa = plotinfo.x(), ya = plotinfo.y(), fullLayout = gd._fullLayout, - id = 'hm' + uid, - cbId = 'cb' + uid; + id = 'hm' + uid; - fullLayout._paper.selectAll('.contour' + uid).remove(); // in case this used to be a contour map + // in case this used to be a contour map + fullLayout._paper.selectAll('.contour' + uid).remove(); if(trace.visible !== true) { fullLayout._paper.selectAll('.' + id).remove(); - fullLayout._paper.selectAll('.' + cbId).remove(); + fullLayout._infolayer.selectAll('.cb' + uid).remove(); return; } From 64d304cd6e21da35408d4402116168dbec954022 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Wed, 27 Jan 2016 14:05:45 -0500 Subject: [PATCH 4/9] lint (mostly space between ops) --- src/traces/contour/plot.js | 17 ++++++++++------- src/traces/heatmap/plot.js | 6 ++++-- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/traces/contour/plot.js b/src/traces/contour/plot.js index f0ed8be4b14..35631ae4607 100644 --- a/src/traces/contour/plot.js +++ b/src/traces/contour/plot.js @@ -70,10 +70,10 @@ function plotOne(gd, plotinfo, cd) { } // use a heatmap to fill - draw it behind the lines - if(contours.coloring==='heatmap') { - if(trace.zauto && trace.autocontour===false) { + if(contours.coloring === 'heatmap') { + if(trace.zauto && (trace.autocontour === false)) { trace._input.zmin = trace.zmin = - contours.start - contours.size/2; + contours.start - contours.size / 2; trace._input.zmax = trace.zmax = trace.zmin + pathinfo.length * contours.size; } @@ -81,7 +81,7 @@ function plotOne(gd, plotinfo, cd) { heatmapPlot(gd, plotinfo, [cd]); } // in case this used to be a heatmap (or have heatmap fill) - else fullLayout._paper.selectAll('.hm'+uid).remove(); + else fullLayout._paper.selectAll('.hm '+ uid).remove(); makeCrossings(pathinfo); findAllPaths(pathinfo); @@ -472,12 +472,15 @@ function getInterpPx(pi, loc, step) { function makeContourGroup(plotinfo, cd, id) { var plotgroup = plotinfo.plot.select('.maplayer') - .selectAll('g.contour.'+id) + .selectAll('g.contour.' + id) .data(cd); + plotgroup.enter().append('g') - .classed('contour',true) - .classed(id,true); + .classed('contour', true) + .classed(id, true); + plotgroup.exit().remove(); + return plotgroup; } diff --git a/src/traces/heatmap/plot.js b/src/traces/heatmap/plot.js index 77de1ed3c65..8709e63da23 100644 --- a/src/traces/heatmap/plot.js +++ b/src/traces/heatmap/plot.js @@ -20,11 +20,13 @@ var xmlnsNamespaces = require('../../constants/xmlns_namespaces'); var maxRowLength = require('./max_row_length'); -// From http://www.xarg.org/2010/03/generate-client-side-png-files-using-javascript/ module.exports = function(gd, plotinfo, cdheatmaps) { - cdheatmaps.forEach(function(cd) { plotOne(gd, plotinfo, cd); }); + for(var i = 0; i < cdheatmaps.length; i++) { + plotOne(gd, plotinfo, cdheatmaps[i]); + } }; +// From http://www.xarg.org/2010/03/generate-client-side-png-files-using-javascript/ function plotOne(gd, plotinfo, cd) { Lib.markTime('in Heatmap.plot'); From 587b2e28701667b146f089151497f62b15a5f713 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Wed, 27 Jan 2016 14:07:05 -0500 Subject: [PATCH 5/9] use d3-omatic pattern in heatmap plot --- src/traces/heatmap/plot.js | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/src/traces/heatmap/plot.js b/src/traces/heatmap/plot.js index 8709e63da23..236fad50b74 100644 --- a/src/traces/heatmap/plot.js +++ b/src/traces/heatmap/plot.js @@ -369,20 +369,28 @@ function plotOne(gd, plotinfo, cd) { gd._hmpixcount = (gd._hmpixcount||0) + pixcount; gd._hmlumcount = (gd._hmlumcount||0) + pixcount * avgColor.getLuminance(); - // put this right before making the new image, to minimize flicker - fullLayout._paper.selectAll('.'+id).remove(); - plotinfo.plot.select('.imagelayer').append('svg:image') - .classed(id, true) - .datum(cd[0]) - .attr({ - xmlns: xmlnsNamespaces.svg, - 'xlink:href': canvas.toDataURL('image/png'), - height: imageHeight, - width: imageWidth, - x: left, - y: top, - preserveAspectRatio: 'none' - }); + var plotgroup = plotinfo.plot.select('.imagelayer') + .selectAll('g.hm.' + id) + .data([0]); + plotgroup.enter().append('g') + .classed('hm', true) + .classed(id, true); + plotgroup.exit().remove(); + + var image3 = plotgroup.selectAll('image') + .data(cd); + image3.enter().append('svg:image'); + image3.exit().remove(); + + image3.attr({ + xmlns: xmlnsNamespaces.svg, + 'xlink:href': canvas.toDataURL('image/png'), + height: imageHeight, + width: imageWidth, + x: left, + y: top, + preserveAspectRatio: 'none' + }); Lib.markTime('done showing png'); } From 075e4950c7941fdcef36c2feeaeecf41842eb0dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Wed, 27 Jan 2016 14:36:37 -0500 Subject: [PATCH 6/9] fixup path in selectors --- src/traces/contour/plot.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/traces/contour/plot.js b/src/traces/contour/plot.js index 35631ae4607..f9f0ccafb4f 100644 --- a/src/traces/contour/plot.js +++ b/src/traces/contour/plot.js @@ -64,7 +64,7 @@ function plotOne(gd, plotinfo, cd) { pathinfo = emptyPathinfo(contours, plotinfo, cd[0]); if(trace.visible !== true) { - fullLayout._paper.selectAll('.' + id + ',.hm' + id).remove(); + fullLayout._paper.selectAll('.' + id + ',.hm' + uid).remove(); fullLayout._infolayer.selectAll('.cb' + uid).remove(); return; } @@ -81,7 +81,7 @@ function plotOne(gd, plotinfo, cd) { heatmapPlot(gd, plotinfo, [cd]); } // in case this used to be a heatmap (or have heatmap fill) - else fullLayout._paper.selectAll('.hm '+ uid).remove(); + else fullLayout._paper.selectAll('.hm' + uid).remove(); makeCrossings(pathinfo); findAllPaths(pathinfo); From fddab04525886675940ac06f8571aa1dab8d6f55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Wed, 27 Jan 2016 17:09:10 -0500 Subject: [PATCH 7/9] remove hm and contour groups on update and colorbar group when necessary. --- src/plot_api/plot_api.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/plot_api/plot_api.js b/src/plot_api/plot_api.js index 2ff8e403f44..38f11655f6c 100644 --- a/src/plot_api/plot_api.js +++ b/src/plot_api/plot_api.js @@ -251,7 +251,7 @@ Plotly.plot = function(gd, data, layout, config) { subplots = Plots.getSubplotIds(fullLayout, 'cartesian'), modules = gd._modules; - var i, j, cd, trace, uid, subplot, subplotInfo, + var i, j, trace, subplot, subplotInfo, cdSubplot, cdError, cdModule, _module; function getCdSubplot(calcdata, subplot) { @@ -293,17 +293,20 @@ Plotly.plot = function(gd, data, layout, config) { // in case of traces that were heatmaps or contour maps // previously, remove them and their colorbars explicitly for (i = 0; i < calcdata.length; i++) { - cd = calcdata[i]; - trace = cd[0].trace; - if (trace.visible !== true || !trace._module.colorbar) { + trace = calcdata[i][0].trace; + + var isVisible = (trace.visible === true), uid = trace.uid; + if(!isVisible || !Plots.traceIs(trace, '2dMap')) { fullLayout._paper.selectAll( '.hm' + uid + ',.contour' + uid + ',#clip' + uid ).remove(); + } + if(!isVisible || !trace._module.colorbar) { fullLayout._infolayer.selectAll('.cb' + uid).remove(); } } From b3e0fae96d0a46363340d569c0cbc57dab02d1dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Wed, 27 Jan 2016 17:09:26 -0500 Subject: [PATCH 8/9] add heatmap/contour interact tests --- test/jasmine/tests/plot_interact_test.js | 96 ++++++++++++++++++++++++ 1 file changed, 96 insertions(+) diff --git a/test/jasmine/tests/plot_interact_test.js b/test/jasmine/tests/plot_interact_test.js index fc9822e70aa..eae10037d55 100644 --- a/test/jasmine/tests/plot_interact_test.js +++ b/test/jasmine/tests/plot_interact_test.js @@ -1,6 +1,7 @@ var d3 = require('d3'); var Plotly = require('@lib/index'); +var Lib = require('@src/lib'); var createGraphDiv = require('../assets/create_graph_div'); var destroyGraphDiv = require('../assets/destroy_graph_div'); @@ -62,6 +63,101 @@ describe('Test plot structure', function() { }); }); + describe('contour/heatmap traces', function() { + var mock = require('@mocks/connectgaps_2d.json'); + + function extendMock() { + var mockCopy = Lib.extendDeep(mock); + + // add a colorbar for testing + mockCopy.data[0].showscale = true; + + return mockCopy; + } + + describe('initial structure', function() { + beforeEach(function(done) { + var mockCopy = extendMock(); + + Plotly.plot(createGraphDiv(), mockCopy.data, mockCopy.layout) + .then(done); + }); + + it('has four *subplot* nodes', function() { + var nodes = d3.selectAll('g.subplot'); + expect(nodes.size()).toEqual(4); + }); + + // N.B. the contour traces both have a heatmap fill + it('has four heatmap image nodes', function() { + var hmNodes = d3.selectAll('g.hm'); + expect(hmNodes.size()).toEqual(4); + + var imageNodes = d3.selectAll('image'); + expect(imageNodes.size()).toEqual(4); + }); + + it('has two contour nodes', function() { + var nodes = d3.selectAll('g.contour'); + expect(nodes.size()).toEqual(2); + }); + + it('has one colorbar nodes', function() { + var nodes = d3.selectAll('rect.cbbg'); + expect(nodes.size()).toEqual(1); + }); + }); + + describe('structure after restyle', function() { + beforeEach(function(done) { + var mockCopy = extendMock(); + var gd = createGraphDiv(); + + Plotly.plot(gd, mockCopy.data, mockCopy.layout); + + Plotly.restyle(gd, { + type: 'scatter', + x: [[1, 2, 3]], + y: [[2, 1, 2]], + z: null + }, 0); + + Plotly.restyle(gd, 'type', 'contour', 1); + + Plotly.restyle(gd, 'type', 'heatmap', 2) + .then(done); + }); + + it('has four *subplot* nodes', function() { + var nodes = d3.selectAll('g.subplot'); + expect(nodes.size()).toEqual(4); + }); + + it('has two heatmap image nodes', function() { + var hmNodes = d3.selectAll('g.hm'); + expect(hmNodes.size()).toEqual(2); + + var imageNodes = d3.selectAll('image'); + expect(imageNodes.size()).toEqual(2); + }); + + it('has two contour nodes', function() { + var nodes = d3.selectAll('g.contour'); + expect(nodes.size()).toEqual(2); + }); + + it('has one scatter node', function() { + var nodes = d3.selectAll('g.trace.scatter'); + expect(nodes.size()).toEqual(1); + }); + + it('has no colorbar node', function() { + var nodes = d3.selectAll('rect.cbbg'); + expect(nodes.size()).toEqual(0); + }); + }); + }); + describe('pie traces', function() { var mock = require('@mocks/pie_simple.json'); From c080c03f520d6a1d72f485dc0b10b765a8c5b3e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Wed, 27 Jan 2016 17:09:47 -0500 Subject: [PATCH 9/9] fix type (restyle --> relayout) --- test/jasmine/tests/plot_promise_test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/jasmine/tests/plot_promise_test.js b/test/jasmine/tests/plot_promise_test.js index b42af7044db..981d5c30d8d 100644 --- a/test/jasmine/tests/plot_promise_test.js +++ b/test/jasmine/tests/plot_promise_test.js @@ -282,7 +282,7 @@ describe('Plotly.___ methods', function() { Plotly.plot(initialDiv, data, {}); - promise = Plotly.restyle(initialDiv, 'title', 'Promise test!'); + promise = Plotly.relayout(initialDiv, 'title', 'Promise test!'); promise.then(function(gd){ promiseGd = gd;