'use strict'; var createLine = require('regl-line2d'); var Registry = require('../../registry'); var prepareRegl = require('../../lib/prepare_regl'); var getModuleCalcData = require('../../plots/get_data').getModuleCalcData; var Cartesian = require('../../plots/cartesian'); var getFromId = require('../../plots/cartesian/axis_ids').getFromId; var shouldShowZeroLine = require('../../plots/cartesian/axes').shouldShowZeroLine; var SPLOM = 'splom'; var reglPrecompiled = {}; function plot(gd) { var fullLayout = gd._fullLayout; var _module = Registry.getModule(SPLOM); var splomCalcData = getModuleCalcData(gd.calcdata, _module)[0]; var success = prepareRegl(gd, ['ANGLE_instanced_arrays', 'OES_element_index_uint'], reglPrecompiled); if(!success) return; if(fullLayout._hasOnlyLargeSploms) { updateGrid(gd); } _module.plot(gd, {}, splomCalcData); } function drag(gd) { var cd = gd.calcdata; var fullLayout = gd._fullLayout; if(fullLayout._hasOnlyLargeSploms) { updateGrid(gd); } for(var i = 0; i < cd.length; i++) { var cd0 = cd[i][0]; var trace = cd0.trace; var scene = fullLayout._splomScenes[trace.uid]; if(trace.type === 'splom' && scene && scene.matrix) { dragOne(gd, trace, scene); } } } function dragOne(gd, trace, scene) { var visibleLength = scene.matrixOptions.data.length; var visibleDims = trace._visibleDims; var ranges = scene.viewOpts.ranges = new Array(visibleLength); for(var k = 0; k < visibleDims.length; k++) { var i = visibleDims[k]; var rng = ranges[k] = new Array(4); var xa = getFromId(gd, trace._diag[i][0]); if(xa) { rng[0] = xa.r2l(xa.range[0]); rng[2] = xa.r2l(xa.range[1]); } var ya = getFromId(gd, trace._diag[i][1]); if(ya) { rng[1] = ya.r2l(ya.range[0]); rng[3] = ya.r2l(ya.range[1]); } } if(scene.selectBatch.length || scene.unselectBatch.length) { scene.matrix.update({ranges: ranges}, {ranges: ranges}); } else { scene.matrix.update({ranges: ranges}); } } function updateGrid(gd) { var fullLayout = gd._fullLayout; var regl = fullLayout._glcanvas.data()[0].regl; var splomGrid = fullLayout._splomGrid; if(!splomGrid) { splomGrid = fullLayout._splomGrid = createLine(regl); } splomGrid.update(makeGridData(gd)); } function makeGridData(gd) { var plotGlPixelRatio = gd._context.plotGlPixelRatio; var fullLayout = gd._fullLayout; var gs = fullLayout._size; var fullView = [ 0, 0, fullLayout.width * plotGlPixelRatio, fullLayout.height * plotGlPixelRatio ]; var lookup = {}; var k; function push(prefix, ax, x0, x1, y0, y1) { x0 *= plotGlPixelRatio; x1 *= plotGlPixelRatio; y0 *= plotGlPixelRatio; y1 *= plotGlPixelRatio; var lcolor = ax[prefix + 'color']; var lwidth = ax[prefix + 'width']; var key = String(lcolor + lwidth); if(key in lookup) { lookup[key].data.push(NaN, NaN, x0, x1, y0, y1); } else { lookup[key] = { data: [x0, x1, y0, y1], join: 'rect', thickness: lwidth * plotGlPixelRatio, color: lcolor, viewport: fullView, range: fullView, overlay: false }; } } for(k in fullLayout._splomSubplots) { var sp = fullLayout._plots[k]; var xa = sp.xaxis; var ya = sp.yaxis; var xVals = xa._gridVals; var yVals = ya._gridVals; var xOffset = xa._offset; var xLength = xa._length; var yLength = ya._length; // ya.l2p assumes top-to-bottom coordinate system (a la SVG), // we need to compute bottom-to-top offsets and slopes: var yOffset = gs.b + ya.domain[0] * gs.h; var ym = -ya._m; var yb = -ym * ya.r2l(ya.range[0], ya.calendar); var x, y; if(xa.showgrid) { for(k = 0; k < xVals.length; k++) { x = xOffset + xa.l2p(xVals[k].x); push('grid', xa, x, yOffset, x, yOffset + yLength); } } if(ya.showgrid) { for(k = 0; k < yVals.length; k++) { y = yOffset + yb + ym * yVals[k].x; push('grid', ya, xOffset, y, xOffset + xLength, y); } } if(shouldShowZeroLine(gd, xa, ya)) { x = xOffset + xa.l2p(0); push('zeroline', xa, x, yOffset, x, yOffset + yLength); } if(shouldShowZeroLine(gd, ya, xa)) { y = yOffset + yb + 0; push('zeroline', ya, xOffset, y, xOffset + xLength, y); } } var gridBatches = []; for(k in lookup) { gridBatches.push(lookup[k]); } return gridBatches; } function clean(newFullData, newFullLayout, oldFullData, oldFullLayout) { var lookup = {}; var i; if(oldFullLayout._splomScenes) { for(i = 0; i < newFullData.length; i++) { var newTrace = newFullData[i]; if(newTrace.type === 'splom') { lookup[newTrace.uid] = 1; } } for(i = 0; i < oldFullData.length; i++) { var oldTrace = oldFullData[i]; if(!lookup[oldTrace.uid]) { var scene = oldFullLayout._splomScenes[oldTrace.uid]; if(scene && scene.destroy) scene.destroy(); // must first set scene to null in order to get garbage collected oldFullLayout._splomScenes[oldTrace.uid] = null; delete oldFullLayout._splomScenes[oldTrace.uid]; } } } if(Object.keys(oldFullLayout._splomScenes || {}).length === 0) { delete oldFullLayout._splomScenes; } if(oldFullLayout._splomGrid && (!newFullLayout._hasOnlyLargeSploms && oldFullLayout._hasOnlyLargeSploms)) { // must first set scene to null in order to get garbage collected oldFullLayout._splomGrid.destroy(); oldFullLayout._splomGrid = null; delete oldFullLayout._splomGrid; } Cartesian.clean(newFullData, newFullLayout, oldFullData, oldFullLayout); } module.exports = { name: SPLOM, attr: Cartesian.attr, attrRegex: Cartesian.attrRegex, layoutAttributes: Cartesian.layoutAttributes, supplyLayoutDefaults: Cartesian.supplyLayoutDefaults, drawFramework: Cartesian.drawFramework, plot: plot, drag: drag, updateGrid: updateGrid, clean: clean, updateFx: Cartesian.updateFx, toSVG: Cartesian.toSVG, reglPrecompiled: reglPrecompiled };