From e85552120990030c2902437ad5175aa877fbd9d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Wed, 18 May 2016 17:57:48 -0400 Subject: [PATCH 1/6] snapshot: clean wait callback - rm unnecessary 'window' - rm commented out line (ne need to attach canvas to window) --- src/plot_api/to_image.js | 8 +++++--- src/snapshot/toimage.js | 8 ++++---- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/plot_api/to_image.js b/src/plot_api/to_image.js index 049ea559e2b..f737a049af7 100644 --- a/src/plot_api/to_image.js +++ b/src/plot_api/to_image.js @@ -61,11 +61,12 @@ function toImage(gd, opts) { setTimeout(function() { var svg = Snapshot.toSVG(clonedGd); - var canvasContainer = window.document.createElement('div'); - var canvas = window.document.createElement('canvas'); + var canvasContainer = document.createElement('div'), + canvas = document.createElement('canvas'); - canvasContainer.appendChild(canvas); + // no need to attach canvas container to DOM + canvasContainer.appendChild(canvas); canvasContainer.id = Plotly.Lib.randstr(); canvas.id = Plotly.Lib.randstr(); @@ -86,6 +87,7 @@ function toImage(gd, opts) { }).catch(function(err) { reject(err); }); + }, delay); }); } diff --git a/src/snapshot/toimage.js b/src/snapshot/toimage.js index d4aef39dfc7..75600e0c4a1 100644 --- a/src/snapshot/toimage.js +++ b/src/snapshot/toimage.js @@ -38,12 +38,12 @@ function toImage(gd, opts) { setTimeout(function() { var svg = Plotly.Snapshot.toSVG(clonedGd); - var canvasContainer = window.document.createElement('div'); - var canvas = window.document.createElement('canvas'); + var canvasContainer = document.createElement('div'), + canvas = document.createElement('canvas'); - // window.document.body.appendChild(canvasContainer); - canvasContainer.appendChild(canvas); + // no need to attach canvas container to DOM + canvasContainer.appendChild(canvas); canvasContainer.id = Plotly.Lib.randstr(); canvas.id = Plotly.Lib.randstr(); From 6344fa41487b4409df74e4b569221e459fac0dd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Wed, 18 May 2016 17:59:22 -0400 Subject: [PATCH 2/6] introduce per-basePlotModule toSVG step: - so that the WebGL -> to step can be generalize to custom base plot modules --- src/plots/geo/index.js | 24 +++++++++++ src/plots/gl2d/index.js | 32 +++++++++++++-- src/plots/gl3d/index.js | 28 +++++++++++++ src/snapshot/tosvg.js | 90 ++++++----------------------------------- 4 files changed, 93 insertions(+), 81 deletions(-) diff --git a/src/plots/geo/index.js b/src/plots/geo/index.js index 5c169c99d4b..cd277b1c9ca 100644 --- a/src/plots/geo/index.js +++ b/src/plots/geo/index.js @@ -78,3 +78,27 @@ exports.clean = function(newFullData, newFullLayout, oldFullData, oldFullLayout) } } }; + +exports.toSVG = function(gd) { + var fullLayout = gd._fullLayout, + geoIds = Plots.getSubplotIds(fullLayout, 'geo'), + size = fullLayout._size; + + for(var i = 0; i < geoIds.length; i++) { + var geoLayout = fullLayout[geoIds[i]], + domain = geoLayout.domain, + geoFramework = geoLayout._geo.framework; + + geoFramework.attr('style', null); + geoFramework + .attr({ + x: size.l + size.w * domain.x[0] + geoLayout._marginX, + y: size.t + size.h * (1 - domain.y[1]) + geoLayout._marginY, + width: geoLayout._width, + height: geoLayout._height + }); + + fullLayout._geoimages.node() + .appendChild(geoFramework.node()); + } +}; diff --git a/src/plots/gl2d/index.js b/src/plots/gl2d/index.js index 61524d3a60b..b07f97341df 100644 --- a/src/plots/gl2d/index.js +++ b/src/plots/gl2d/index.js @@ -9,11 +9,9 @@ 'use strict'; -var Plotly = require('../../plotly'); - var Scene2D = require('./scene2d'); - -var Plots = Plotly.Plots; +var Plots = require('../plots'); +var xmlnsNamespaces = require('../../constants/xmlns_namespaces'); exports.name = 'gl2d'; @@ -80,3 +78,29 @@ exports.clean = function(newFullData, newFullLayout, oldFullData, oldFullLayout) } } }; + +exports.toSVG = function(gd) { + var fullLayout = gd._fullLayout, + subplotIds = Plots.getSubplotIds(fullLayout, 'gl2d'), + size = fullLayout._size; + + for(var i = 0; i < subplotIds.length; i++) { + var subplot = fullLayout._plots[subplotIds[i]], + scene = subplot._scene2d; + + var imageData = scene.toImage('png'); + var image = fullLayout._glimages.append('svg:image'); + + image.attr({ + xmlns: xmlnsNamespaces.svg, + 'xlink:href': imageData, + x: size.l, + y: size.t, + width: size.w, + height: size.h, + preserveAspectRatio: 'none' + }); + + scene.destroy(); + } +}; diff --git a/src/plots/gl3d/index.js b/src/plots/gl3d/index.js index be90dab79d3..7d5134a23d3 100644 --- a/src/plots/gl3d/index.js +++ b/src/plots/gl3d/index.js @@ -11,6 +11,7 @@ var Scene = require('./scene'); var Plots = require('../plots'); +var xmlnsNamespaces = require('../../constants/xmlns_namespaces'); var axesNames = ['xaxis', 'yaxis', 'zaxis']; @@ -83,6 +84,33 @@ exports.clean = function(newFullData, newFullLayout, oldFullData, oldFullLayout) } }; +exports.toSVG = function(gd) { + var fullLayout = gd._fullLayout, + sceneIds = Plots.getSubplotIds(fullLayout, 'gl3d'), + size = fullLayout._size; + + for(var i = 0; i < sceneIds.length; i++) { + var sceneLayout = fullLayout[sceneIds[i]], + domain = sceneLayout.domain, + scene = sceneLayout._scene; + + var imageData = scene.toImage('png'); + var image = fullLayout._glimages.append('svg:image'); + + image.attr({ + xmlns: xmlnsNamespaces.svg, + 'xlink:href': imageData, + x: size.l + size.w * domain.x[0], + y: size.t + size.h * (1 - domain.y[1]), + width: size.w * (domain.x[1] - domain.x[0]), + height: size.h * (domain.y[1] - domain.y[0]), + preserveAspectRatio: 'none' + }); + + scene.destroy(); + } +}; + // clean scene ids, 'scene1' -> 'scene' exports.cleanId = function cleanId(id) { if(!id.match(/^scene[0-9]*$/)) return; diff --git a/src/snapshot/tosvg.js b/src/snapshot/tosvg.js index eea9789c545..206db941a0a 100644 --- a/src/snapshot/tosvg.js +++ b/src/snapshot/tosvg.js @@ -11,7 +11,6 @@ var d3 = require('d3'); -var Plots = require('../plots/plots'); var svgTextUtils = require('../lib/svg_text_utils'); var Drawing = require('../components/drawing'); var Color = require('../components/color'); @@ -20,80 +19,34 @@ var xmlnsNamespaces = require('../constants/xmlns_namespaces'); module.exports = function toSVG(gd, format) { + var fullLayout = gd._fullLayout, + svg = fullLayout._paper, + toppaper = fullLayout._toppaper, + i; // make background color a rect in the svg, then revert after scraping // all other alterations have been dealt with by properly preparing the svg // in the first place... like setting cursors with css classes so we don't // have to remove them, and providing the right namespaces in the svg to // begin with - var fullLayout = gd._fullLayout, - svg = fullLayout._paper, - size = fullLayout._size, - domain, - i; - svg.insert('rect', ':first-child') .call(Drawing.setRect, 0, 0, fullLayout.width, fullLayout.height) .call(Color.fill, fullLayout.paper_bgcolor); - /* Grab the 3d scenes and rasterize em. Calculate their positions, - * then insert them into the SVG element as images */ - var sceneIds = Plots.getSubplotIds(fullLayout, 'gl3d'), - scene; - - for(i = 0; i < sceneIds.length; i++) { - scene = fullLayout[sceneIds[i]]; - domain = scene.domain; - insertGlImage(fullLayout, scene._scene, { - x: size.l + size.w * domain.x[0], - y: size.t + size.h * (1 - domain.y[1]), - width: size.w * (domain.x[1] - domain.x[0]), - height: size.h * (domain.y[1] - domain.y[0]) - }); - } - - // similarly for 2d scenes - var subplotIds = Plots.getSubplotIds(fullLayout, 'gl2d'), - subplot; - - for(i = 0; i < subplotIds.length; i++) { - subplot = fullLayout._plots[subplotIds[i]]; - insertGlImage(fullLayout, subplot._scene2d, { - x: size.l, - y: size.t, - width: size.w, - height: size.h - }); - } + // subplot-specific to-SVG methods + // which notably add the contents of the gl-container + // into the main svg node + var basePlotModules = fullLayout._basePlotModules || []; + for(i = 0; i < basePlotModules.length; i++) { + var _module = basePlotModules[i]; - // Grab the geos off the geo-container and place them in geoimages - var geoIds = Plots.getSubplotIds(fullLayout, 'geo'), - geoLayout, - geoFramework; - - for(i = 0; i < geoIds.length; i++) { - geoLayout = fullLayout[geoIds[i]]; - domain = geoLayout.domain; - geoFramework = geoLayout._geo.framework; - - geoFramework.attr('style', null); - geoFramework - .attr({ - x: size.l + size.w * domain.x[0] + geoLayout._marginX, - y: size.t + size.h * (1 - domain.y[1]) + geoLayout._marginY, - width: geoLayout._width, - height: geoLayout._height - }); - - fullLayout._geoimages.node() - .appendChild(geoFramework.node()); + if(_module.toSVG) _module.toSVG(gd); } - // now that we've got the 3d images in the right layer, // add top items above them assumes everything in toppaper is either // a group or a defs, and if it's empty (like hoverlayer) we can ignore it. - if(fullLayout._toppaper) { - var nodes = fullLayout._toppaper.node().childNodes; + if(toppaper) { + var nodes = toppaper.node().childNodes; // make copy of nodes as childNodes prop gets mutated in loop below var topGroups = Array.prototype.slice.call(nodes); @@ -164,20 +117,3 @@ module.exports = function toSVG(gd, format) { return s; }; - -function insertGlImage(fullLayout, scene, opts) { - var imageData = scene.toImage('png'); - - fullLayout._glimages.append('svg:image') - .attr({ - xmlns: xmlnsNamespaces.svg, - 'xlink:href': imageData, - x: opts.x, - y: opts.y, - width: opts.width, - height: opts.height, - preserveAspectRatio: 'none' - }); - - scene.destroy(); -} From ab1dcb3d091b0516e6d5ee2ea16467c7eac95c0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Thu, 19 May 2016 14:41:17 -0400 Subject: [PATCH 3/6] cleanup if-visible block(s): - rm obsolete '|| scene' condition - merge multiple if-visible into one --- src/plots/plots.js | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/plots/plots.js b/src/plots/plots.js index 295755768ae..a9878381671 100644 --- a/src/plots/plots.js +++ b/src/plots/plots.js @@ -658,9 +658,7 @@ plots.supplyDataDefaults = function(traceIn, i, layout) { // module-independent attributes traceOut.index = i; - var visible = coerce('visible'), - scene, - _module; + var visible = coerce('visible'); coerce('type'); coerce('uid'); @@ -672,19 +670,20 @@ plots.supplyDataDefaults = function(traceIn, i, layout) { coerceSubplotAttr('geo', 'geo'); coerceSubplotAttr('ternary', 'subplot'); - // module-specific attributes --- note: we need to send a trace into - // the 3D modules to have it removed from the webgl context. - if(visible || scene) { - _module = plots.getModule(traceOut); - traceOut._module = _module; - } - // gets overwritten in pie, geo and ternary modules - if(visible) coerce('hoverinfo', (layout._dataLength === 1) ? 'x+y+z+text' : undefined); - if(_module && visible) _module.supplyDefaults(traceIn, traceOut, defaultColor, layout); if(visible) { + var _module = plots.getModule(traceOut); + traceOut._module = _module; + + // gets overwritten in pie, geo and ternary modules + coerce('hoverinfo', (layout._dataLength === 1) ? 'x+y+z+text' : undefined); + + // TODO add per-base-plot-module trace defaults step + + if(_module) _module.supplyDefaults(traceIn, traceOut, defaultColor, layout); + coerce('name', 'trace ' + i); if(!plots.traceIs(traceOut, 'noOpacity')) coerce('opacity'); From 6d35c4899fcfbf1b21d1f78731af8c8103f747c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Thu, 19 May 2016 14:47:35 -0400 Subject: [PATCH 4/6] rename trace index 'i' -> traceIndex --- src/plots/plots.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/plots/plots.js b/src/plots/plots.js index a9878381671..9ad0ff1f6a2 100644 --- a/src/plots/plots.js +++ b/src/plots/plots.js @@ -642,9 +642,9 @@ function relinkPrivateKeys(toLayout, fromLayout) { } } -plots.supplyDataDefaults = function(traceIn, i, layout) { +plots.supplyDataDefaults = function(traceIn, traceIndex, layout) { var traceOut = {}, - defaultColor = Color.defaults[i % Color.defaults.length]; + defaultColor = Color.defaults[traceIndex % Color.defaults.length]; function coerce(attr, dflt) { return Lib.coerce(traceIn, traceOut, plots.attributes, attr, dflt); @@ -652,12 +652,13 @@ plots.supplyDataDefaults = function(traceIn, i, layout) { function coerceSubplotAttr(subplotType, subplotAttr) { if(!plots.traceIs(traceOut, subplotType)) return; + return Lib.coerce(traceIn, traceOut, plots.subplotsRegistry[subplotType].attributes, subplotAttr); } // module-independent attributes - traceOut.index = i; + traceOut.index = traceIndex; var visible = coerce('visible'); coerce('type'); @@ -684,7 +685,7 @@ plots.supplyDataDefaults = function(traceIn, i, layout) { if(_module) _module.supplyDefaults(traceIn, traceOut, defaultColor, layout); - coerce('name', 'trace ' + i); + coerce('name', 'trace ' + traceIndex); if(!plots.traceIs(traceOut, 'noOpacity')) coerce('opacity'); From 68a6a96f77151b7099246c559522e6241eebd720 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Thu, 19 May 2016 14:50:57 -0400 Subject: [PATCH 5/6] loop over register subplot types to coerce their subplot attr --- src/plots/plots.js | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/plots/plots.js b/src/plots/plots.js index 9ad0ff1f6a2..d091511bffe 100644 --- a/src/plots/plots.js +++ b/src/plots/plots.js @@ -664,15 +664,19 @@ plots.supplyDataDefaults = function(traceIn, traceIndex, layout) { coerce('type'); coerce('uid'); - // this is necessary otherwise we lose references to scene objects when - // the traces of a scene are invisible. Also we handle visible/unvisible - // differently for 3D cases. - coerceSubplotAttr('gl3d', 'scene'); - coerceSubplotAttr('geo', 'geo'); - coerceSubplotAttr('ternary', 'subplot'); + // coerce subplot attributes of all registered subplot types + var subplotTypes = Object.keys(subplotsRegistry); + for(var i = 0; i < subplotTypes.length; i++) { + var subplotType = subplotTypes[i]; + // done below (only when visible is true) + // TODO unified this pattern + if(['cartesian', 'gl2d'].indexOf(subplotType) !== -1) continue; + var attr = subplotsRegistry[subplotType].attr; + if(attr) coerceSubplotAttr(subplotType, attr); + } if(visible) { var _module = plots.getModule(traceOut); From b0f623034a9023bea0bdbb72a76c1fe16ccc7f1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Thu, 19 May 2016 18:52:05 -0400 Subject: [PATCH 6/6] rm uselss canvas container in to-image --- src/plot_api/to_image.js | 14 +++++--------- src/snapshot/toimage.js | 15 +++++---------- 2 files changed, 10 insertions(+), 19 deletions(-) diff --git a/src/plot_api/to_image.js b/src/plot_api/to_image.js index f737a049af7..0da4a7dbd48 100644 --- a/src/plot_api/to_image.js +++ b/src/plot_api/to_image.js @@ -8,9 +8,11 @@ 'use strict'; +var isNumeric = require('fast-isnumeric'); + var Plotly = require('../plotly'); +var Lib = require('../lib'); -var isNumeric = require('fast-isnumeric'); /** * @param {object} gd figure Object @@ -61,14 +63,8 @@ function toImage(gd, opts) { setTimeout(function() { var svg = Snapshot.toSVG(clonedGd); - var canvasContainer = document.createElement('div'), - canvas = document.createElement('canvas'); - - // no need to attach canvas container to DOM - - canvasContainer.appendChild(canvas); - canvasContainer.id = Plotly.Lib.randstr(); - canvas.id = Plotly.Lib.randstr(); + var canvas = document.createElement('canvas'); + canvas.id = Lib.randstr(); Snapshot.svgToImg({ format: opts.format, diff --git a/src/snapshot/toimage.js b/src/snapshot/toimage.js index 75600e0c4a1..b66ab3eacc5 100644 --- a/src/snapshot/toimage.js +++ b/src/snapshot/toimage.js @@ -6,12 +6,13 @@ * LICENSE file in the root directory of this source tree. */ -/*eslint dot-notation: [2, {"allowPattern": "^catch$"}]*/ - 'use strict'; var EventEmitter = require('events').EventEmitter; + var Plotly = require('../plotly'); +var Lib = require('../lib'); + /** * @param {object} gd figure Object @@ -38,14 +39,8 @@ function toImage(gd, opts) { setTimeout(function() { var svg = Plotly.Snapshot.toSVG(clonedGd); - var canvasContainer = document.createElement('div'), - canvas = document.createElement('canvas'); - - // no need to attach canvas container to DOM - - canvasContainer.appendChild(canvas); - canvasContainer.id = Plotly.Lib.randstr(); - canvas.id = Plotly.Lib.randstr(); + var canvas = document.createElement('canvas'); + canvas.id = Lib.randstr(); ev = Plotly.Snapshot.svgToImg({ format: opts.format,