diff --git a/src/plot_api/subroutines.js b/src/plot_api/subroutines.js
index cdd067714bb..9efcca25773 100644
--- a/src/plot_api/subroutines.js
+++ b/src/plot_api/subroutines.js
@@ -520,7 +520,7 @@ exports.doModeBar = function(gd) {
for(var i = 0; i < fullLayout._basePlotModules.length; i++) {
var updateFx = fullLayout._basePlotModules[i].updateFx;
- if(updateFx) updateFx(fullLayout);
+ if(updateFx) updateFx(gd);
}
return Plots.previousPromises(gd);
diff --git a/src/plots/cartesian/axis_defaults.js b/src/plots/cartesian/axis_defaults.js
index dad861716d4..06de3b99378 100644
--- a/src/plots/cartesian/axis_defaults.js
+++ b/src/plots/cartesian/axis_defaults.js
@@ -33,8 +33,8 @@ var setConvert = require('./set_convert');
*/
module.exports = function handleAxisDefaults(containerIn, containerOut, coerce, options, layoutOut) {
var letter = options.letter;
- var id = containerOut._id;
var font = options.font || {};
+ var splomStash = options.splomStash || {};
var visible = coerce('visible', !options.cheateronly);
@@ -66,7 +66,7 @@ module.exports = function handleAxisDefaults(containerIn, containerOut, coerce,
// template too.
var dfltFontColor = (dfltColor !== layoutAttributes.color.dflt) ? dfltColor : font.color;
// try to get default title from splom trace, fallback to graph-wide value
- var dfltTitle = ((layoutOut._splomAxes || {})[letter] || {})[id] || layoutOut._dfltTitle[letter];
+ var dfltTitle = splomStash.label || layoutOut._dfltTitle[letter];
coerce('title', dfltTitle);
Lib.coerceFont(coerce, 'titlefont', {
diff --git a/src/plots/cartesian/graph_interact.js b/src/plots/cartesian/graph_interact.js
index 2ed10621c1c..69dc718bf50 100644
--- a/src/plots/cartesian/graph_interact.js
+++ b/src/plots/cartesian/graph_interact.js
@@ -151,7 +151,7 @@ exports.initInteractions = function initInteractions(gd) {
gd._fullLayout._lasthover.onmousedown(evt);
};
- exports.updateFx(fullLayout);
+ exports.updateFx(gd);
};
// Minimal set of update needed on 'modebar' edits.
@@ -159,7 +159,8 @@ exports.initInteractions = function initInteractions(gd) {
//
// Note that changing the axis configuration and/or the fixedrange attribute
// should trigger a full initInteractions.
-exports.updateFx = function(fullLayout) {
+exports.updateFx = function(gd) {
+ var fullLayout = gd._fullLayout;
var cursor = fullLayout.dragmode === 'pan' ? 'move' : 'crosshair';
setCursor(fullLayout._draggers, cursor);
};
diff --git a/src/plots/cartesian/layout_defaults.js b/src/plots/cartesian/layout_defaults.js
index 6c2f824781e..63abbc2364b 100644
--- a/src/plots/cartesian/layout_defaults.js
+++ b/src/plots/cartesian/layout_defaults.js
@@ -157,7 +157,9 @@ module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) {
axLayoutOut._annIndices = [];
axLayoutOut._shapeIndices = [];
- handleTypeDefaults(axLayoutIn, axLayoutOut, coerce, traces, axName);
+ // set up some private properties
+ axLayoutOut._name = axName;
+ var id = axLayoutOut._id = name2id(axName);
var overlayableAxes = getOverlayableAxes(axLetter, axName);
@@ -170,9 +172,11 @@ module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) {
bgColor: bgColor,
calendar: layoutOut.calendar,
automargin: true,
- cheateronly: axLetter === 'x' && xaCheater[axName] && !xaNonCheater[axName]
+ cheateronly: axLetter === 'x' && xaCheater[axName] && !xaNonCheater[axName],
+ splomStash: ((layoutOut._splomAxes || {})[axLetter] || {})[id]
};
+ handleTypeDefaults(axLayoutIn, axLayoutOut, coerce, defaultOptions);
handleAxisDefaults(axLayoutIn, axLayoutOut, coerce, defaultOptions, layoutOut);
var spikecolor = coerce2('spikecolor'),
diff --git a/src/plots/cartesian/type_defaults.js b/src/plots/cartesian/type_defaults.js
index 5a9414054b6..1234f8a24a6 100644
--- a/src/plots/cartesian/type_defaults.js
+++ b/src/plots/cartesian/type_defaults.js
@@ -6,32 +6,24 @@
* LICENSE file in the root directory of this source tree.
*/
-
'use strict';
var Registry = require('../../registry');
var autoType = require('./axis_autotype');
-var name2id = require('./axis_ids').name2id;
/*
* data: the plot data to use in choosing auto type
* name: axis object name (ie 'xaxis') if one should be stored
*/
-module.exports = function handleTypeDefaults(containerIn, containerOut, coerce, data, name) {
- // set up some private properties
- if(name) {
- containerOut._name = name;
- containerOut._id = name2id(name);
- }
+module.exports = function handleTypeDefaults(containerIn, containerOut, coerce, options) {
+ var axType = coerce('type', (options.splomStash || {}).type);
- var axType = coerce('type');
if(axType === '-') {
- setAutoType(containerOut, data);
+ setAutoType(containerOut, options.data);
if(containerOut.type === '-') {
containerOut.type = 'linear';
- }
- else {
+ } else {
// copy autoType back to input axis
// note that if this object didn't exist
// in the input layout, we have to put it in
@@ -89,9 +81,10 @@ function setAutoType(ax, data) {
}
else if(d0.type === 'splom') {
var dimensions = d0.dimensions;
+ var diag = d0._diag;
for(i = 0; i < dimensions.length; i++) {
var dim = dimensions[i];
- if(dim.visible) {
+ if(dim.visible && (diag[i][0] === id || diag[i][1] === id)) {
ax.type = autoType(dim.values, calendar);
break;
}
diff --git a/src/plots/geo/index.js b/src/plots/geo/index.js
index d6468d7508d..a393f0b5b82 100644
--- a/src/plots/geo/index.js
+++ b/src/plots/geo/index.js
@@ -78,7 +78,8 @@ exports.clean = function(newFullData, newFullLayout, oldFullData, oldFullLayout)
}
};
-exports.updateFx = function(fullLayout) {
+exports.updateFx = function(gd) {
+ var fullLayout = gd._fullLayout;
var subplotIds = fullLayout._subplots[GEO];
for(var i = 0; i < subplotIds.length; i++) {
diff --git a/src/plots/gl2d/index.js b/src/plots/gl2d/index.js
index 26bb7fdc06e..f56572b8bac 100644
--- a/src/plots/gl2d/index.js
+++ b/src/plots/gl2d/index.js
@@ -138,7 +138,8 @@ exports.toSVG = function(gd) {
}
};
-exports.updateFx = function(fullLayout) {
+exports.updateFx = function(gd) {
+ var fullLayout = gd._fullLayout;
var subplotIds = fullLayout._subplots.gl2d;
for(var i = 0; i < subplotIds.length; i++) {
diff --git a/src/plots/gl3d/index.js b/src/plots/gl3d/index.js
index 4c094e6519a..3c4dccbfa2e 100644
--- a/src/plots/gl3d/index.js
+++ b/src/plots/gl3d/index.js
@@ -129,7 +129,8 @@ exports.cleanId = function cleanId(id) {
return SCENE + sceneNum;
};
-exports.updateFx = function(fullLayout) {
+exports.updateFx = function(gd) {
+ var fullLayout = gd._fullLayout;
var subplotIds = fullLayout._subplots[GL3D];
for(var i = 0; i < subplotIds.length; i++) {
diff --git a/src/plots/gl3d/layout/axis_defaults.js b/src/plots/gl3d/layout/axis_defaults.js
index a3ac9c14285..e28b1a74de4 100644
--- a/src/plots/gl3d/layout/axis_defaults.js
+++ b/src/plots/gl3d/layout/axis_defaults.js
@@ -39,7 +39,7 @@ module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, options) {
containerOut._id = axName[0] + options.scene;
containerOut._name = axName;
- handleTypeDefaults(containerIn, containerOut, coerce, options.data);
+ handleTypeDefaults(containerIn, containerOut, coerce, options);
handleAxisDefaults(
containerIn,
diff --git a/src/plots/mapbox/index.js b/src/plots/mapbox/index.js
index dc19d1c5dca..cda8cd931cd 100644
--- a/src/plots/mapbox/index.js
+++ b/src/plots/mapbox/index.js
@@ -152,7 +152,8 @@ function findAccessToken(gd, mapboxIds) {
throw new Error(constants.noAccessTokenErrorMsg);
}
-exports.updateFx = function(fullLayout) {
+exports.updateFx = function(gd) {
+ var fullLayout = gd._fullLayout;
var subplotIds = fullLayout._subplots[MAPBOX];
for(var i = 0; i < subplotIds.length; i++) {
diff --git a/src/traces/splom/attributes.js b/src/traces/splom/attributes.js
index 0c5dd6a9185..dbc9169bbe4 100644
--- a/src/traces/splom/attributes.js
+++ b/src/traces/splom/attributes.js
@@ -58,6 +58,23 @@ module.exports = {
description: 'Sets the dimension values to be plotted.'
},
+ axis: {
+ type: {
+ valType: 'enumerated',
+ values: ['linear', 'log', 'date', 'category'],
+ role: 'info',
+ editType: 'calc+clearAxisTypes',
+ description: [
+ 'Sets the axis type for this dimension\'s generated',
+ 'x and y axes.',
+ 'Note that the axis `type` values set in layout take',
+ 'precedence over this attribute.'
+ ].join(' ')
+ },
+
+ editType: 'calc+clearAxisTypes'
+ },
+
// TODO should add an attribute to pin down x only vars and y only vars
// like https://seaborn.pydata.org/generated/seaborn.pairplot.html
// x_vars and y_vars
diff --git a/src/traces/splom/base_plot.js b/src/traces/splom/base_plot.js
index 80bef941fb1..a91f83b7a67 100644
--- a/src/traces/splom/base_plot.js
+++ b/src/traces/splom/base_plot.js
@@ -45,36 +45,34 @@ function drag(gd) {
for(var i = 0; i < cd.length; i++) {
var cd0 = cd[i][0];
var trace = cd0.trace;
- var scene = cd0.t._scene;
+ var stash = cd0.t;
+ var scene = stash._scene;
if(trace.type === 'splom' && scene && scene.matrix) {
- dragOne(gd, trace, scene);
+ dragOne(gd, trace, stash, scene);
}
}
}
-function dragOne(gd, trace, scene) {
- var dimensions = trace.dimensions;
+function dragOne(gd, trace, stash, scene) {
var visibleLength = scene.matrixOptions.data.length;
+ var visibleDims = stash.visibleDims;
var ranges = new Array(visibleLength);
- for(var i = 0, k = 0; i < dimensions.length; i++) {
- if(dimensions[i].visible) {
- var rng = ranges[k] = new Array(4);
+ for(var k = 0; k < visibleDims.length; k++) {
+ var i = visibleDims[k];
+ var rng = ranges[k] = new Array(4);
- var xa = AxisIDs.getFromId(gd, trace._diag[i][0]);
- if(xa) {
- rng[0] = xa.r2l(xa.range[0]);
- rng[2] = xa.r2l(xa.range[1]);
- }
-
- var ya = AxisIDs.getFromId(gd, trace._diag[i][1]);
- if(ya) {
- rng[1] = ya.r2l(ya.range[0]);
- rng[3] = ya.r2l(ya.range[1]);
- }
+ var xa = AxisIDs.getFromId(gd, trace._diag[i][0]);
+ if(xa) {
+ rng[0] = xa.r2l(xa.range[0]);
+ rng[2] = xa.r2l(xa.range[1]);
+ }
- k++;
+ var ya = AxisIDs.getFromId(gd, trace._diag[i][1]);
+ if(ya) {
+ rng[1] = ya.r2l(ya.range[0]);
+ rng[3] = ya.r2l(ya.range[1]);
}
}
@@ -229,6 +227,30 @@ function clean(newFullData, newFullLayout, oldFullData, oldFullLayout, oldCalcda
Cartesian.clean(newFullData, newFullLayout, oldFullData, oldFullLayout);
}
+function updateFx(gd) {
+ Cartesian.updateFx(gd);
+
+ var fullLayout = gd._fullLayout;
+ var dragmode = fullLayout.dragmode;
+
+ // unset selection styles when coming out of a selection mode
+ if(dragmode === 'zoom' || dragmode === 'pan') {
+ var cd = gd.calcdata;
+
+ for(var i = 0; i < cd.length; i++) {
+ var cd0 = cd[i][0];
+ var trace = cd0.trace;
+
+ if(trace.type === 'splom') {
+ var scene = cd0.t._scene;
+ if(scene.selectBatch === null) {
+ scene.matrix.update(scene.matrixOptions, null);
+ }
+ }
+ }
+ }
+}
+
module.exports = {
name: SPLOM,
attr: Cartesian.attr,
@@ -239,6 +261,6 @@ module.exports = {
plot: plot,
drag: drag,
clean: clean,
- updateFx: Cartesian.updateFx,
+ updateFx: updateFx,
toSVG: Cartesian.toSVG
};
diff --git a/src/traces/splom/defaults.js b/src/traces/splom/defaults.js
index afcb002e8b3..4d0d9c8dc28 100644
--- a/src/traces/splom/defaults.js
+++ b/src/traces/splom/defaults.js
@@ -61,6 +61,8 @@ function dimensionDefaults(dimIn, dimOut) {
if(!(values && values.length)) dimOut.visible = false;
else coerce('visible');
+
+ coerce('axis.type');
}
function handleAxisDefaults(traceIn, traceOut, layout, coerce) {
@@ -113,14 +115,14 @@ function handleAxisDefaults(traceIn, traceOut, layout, coerce) {
for(i = 0; i < dimLength; i++) {
var dim = dimensions[i];
- var xa = xaxes[i + xShift];
- var ya = yaxes[i + yShift];
+ var xaId = xaxes[i + xShift];
+ var yaId = yaxes[i + yShift];
- fillAxisStash(layout, xa, dim);
- fillAxisStash(layout, ya, dim);
+ fillAxisStash(layout, xaId, dim);
+ fillAxisStash(layout, yaId, dim);
// note that some the entries here may be undefined
- diag[i] = [xa, ya];
+ diag[i] = [xaId, yaId];
}
// when lower half is omitted, override grid default
@@ -148,7 +150,13 @@ function fillAxisStash(layout, axId, dim) {
var stash = layout._splomAxes[axLetter];
if(!(axId in stash)) {
- stash[axId] = (dim || {}).label || '';
+ var s = stash[axId] = {};
+ if(dim) {
+ s.label = dim.label || '';
+ if(dim.visible && dim.axis) {
+ s.type = dim.axis.type;
+ }
+ }
}
}
diff --git a/src/traces/splom/index.js b/src/traces/splom/index.js
index c5613e5844f..902646cb17a 100644
--- a/src/traces/splom/index.js
+++ b/src/traces/splom/index.js
@@ -36,20 +36,48 @@ function calc(gd, trace) {
// only differ here for log axes, pass ldata to createMatrix as 'data'
var cdata = opts.cdata = [];
var ldata = opts.data = [];
- var i, k, dim;
+ // keep track of visible dimensions
+ var visibleDims = stash.visibleDims = [];
+ var i, k, dim, xa, ya;
+
+ function makeCalcdata(ax, dim) {
+ // call makeCalcdata with fake input
+ var ccol = ax.makeCalcdata({
+ v: dim.values,
+ vcalendar: trace.calendar
+ }, 'v');
+
+ for(var j = 0; j < ccol.length; j++) {
+ ccol[j] = ccol[j] === BADNUM ? NaN : ccol[j];
+ }
+ cdata.push(ccol);
+ ldata.push(ax.type === 'log' ? Lib.simpleMap(ccol, ax.c2l) : ccol);
+ }
for(i = 0; i < dimensions.length; i++) {
dim = dimensions[i];
if(dim.visible) {
- var axId = trace._diag[i][0] || trace._diag[i][1];
- var ax = AxisIDs.getFromId(gd, axId);
- if(ax) {
- var ccol = makeCalcdata(ax, trace, dim);
- var lcol = ax.type === 'log' ? Lib.simpleMap(ccol, ax.c2l) : ccol;
- cdata.push(ccol);
- ldata.push(lcol);
+ xa = AxisIDs.getFromId(gd, trace._diag[i][0]);
+ ya = AxisIDs.getFromId(gd, trace._diag[i][1]);
+
+ // if corresponding x & y axes don't have matching types, skip dim
+ if(xa && ya && xa.type !== ya.type) {
+ Lib.log('Skipping splom dimension ' + i + ' with conflicting axis types');
+ continue;
+ }
+
+ if(xa) {
+ makeCalcdata(xa, dim);
+ if(ya && ya.type === 'category') {
+ ya._categories = xa._categories.slice();
+ }
+ } else {
+ // should not make it here, if both xa and ya undefined
+ makeCalcdata(ya, dim);
}
+
+ visibleDims.push(i);
}
}
@@ -59,26 +87,24 @@ function calc(gd, trace) {
var visibleLength = cdata.length;
var hasTooManyPoints = (visibleLength * commonLength) > TOO_MANY_POINTS;
- for(i = 0, k = 0; i < dimensions.length; i++) {
+ for(k = 0; k < visibleDims.length; k++) {
+ i = visibleDims[k];
dim = dimensions[i];
- if(dim.visible) {
- var xa = AxisIDs.getFromId(gd, trace._diag[i][0]) || {};
- var ya = AxisIDs.getFromId(gd, trace._diag[i][1]) || {};
-
- // Reuse SVG scatter axis expansion routine.
- // For graphs with very large number of points and array marker.size,
- // use average marker size instead to speed things up.
- var ppad;
- if(hasTooManyPoints) {
- ppad = 2 * (opts.sizeAvg || Math.max(opts.size, 3));
- } else {
- ppad = calcMarkerSize(trace, commonLength);
- }
-
- calcAxisExpansion(gd, trace, xa, ya, cdata[k], cdata[k], ppad);
- k++;
+ xa = AxisIDs.getFromId(gd, trace._diag[i][0]) || {};
+ ya = AxisIDs.getFromId(gd, trace._diag[i][1]) || {};
+
+ // Reuse SVG scatter axis expansion routine.
+ // For graphs with very large number of points and array marker.size,
+ // use average marker size instead to speed things up.
+ var ppad;
+ if(hasTooManyPoints) {
+ ppad = 2 * (opts.sizeAvg || Math.max(opts.size, 3));
+ } else {
+ ppad = calcMarkerSize(trace, commonLength);
}
+
+ calcAxisExpansion(gd, trace, xa, ya, cdata[k], cdata[k], ppad);
}
var scene = stash._scene = sceneUpdate(gd, stash);
@@ -91,20 +117,6 @@ function calc(gd, trace) {
return [{x: false, y: false, t: stash, trace: trace}];
}
-function makeCalcdata(ax, trace, dim) {
- // call makeCalcdata with fake input
- var ccol = ax.makeCalcdata({
- v: dim.values,
- vcalendar: trace.calendar
- }, 'v');
-
- for(var i = 0; i < ccol.length; i++) {
- ccol[i] = ccol[i] === BADNUM ? NaN : ccol[i];
- }
-
- return ccol;
-}
-
function sceneUpdate(gd, stash) {
var scene = stash._scene;
@@ -126,9 +138,7 @@ function sceneUpdate(gd, stash) {
// draw traces in selection mode
if(scene.matrix && scene.selectBatch) {
scene.matrix.draw(scene.unselectBatch, scene.selectBatch);
- }
-
- else if(scene.matrix) {
+ } else if(scene.matrix) {
scene.matrix.draw();
}
@@ -184,34 +194,32 @@ function plotOne(gd, cd0) {
matrixOpts.upper = trace.showlowerhalf;
matrixOpts.diagonal = trace.diagonal.visible;
- var dimensions = trace.dimensions;
+ var visibleDims = stash.visibleDims;
var visibleLength = cdata.length;
var viewOpts = {};
viewOpts.ranges = new Array(visibleLength);
viewOpts.domains = new Array(visibleLength);
- for(i = 0, k = 0; i < dimensions.length; i++) {
- if(trace.dimensions[i].visible) {
- var rng = viewOpts.ranges[k] = new Array(4);
- var dmn = viewOpts.domains[k] = new Array(4);
+ for(k = 0; k < visibleDims.length; k++) {
+ i = visibleDims[k];
- xa = AxisIDs.getFromId(gd, trace._diag[i][0]);
- if(xa) {
- rng[0] = xa._rl[0];
- rng[2] = xa._rl[1];
- dmn[0] = xa.domain[0];
- dmn[2] = xa.domain[1];
- }
+ var rng = viewOpts.ranges[k] = new Array(4);
+ var dmn = viewOpts.domains[k] = new Array(4);
- ya = AxisIDs.getFromId(gd, trace._diag[i][1]);
- if(ya) {
- rng[1] = ya._rl[0];
- rng[3] = ya._rl[1];
- dmn[1] = ya.domain[0];
- dmn[3] = ya.domain[1];
- }
+ xa = AxisIDs.getFromId(gd, trace._diag[i][0]);
+ if(xa) {
+ rng[0] = xa._rl[0];
+ rng[2] = xa._rl[1];
+ dmn[0] = xa.domain[0];
+ dmn[2] = xa.domain[1];
+ }
- k++;
+ ya = AxisIDs.getFromId(gd, trace._diag[i][1]);
+ if(ya) {
+ rng[1] = ya._rl[0];
+ rng[3] = ya._rl[1];
+ dmn[1] = ya.domain[0];
+ dmn[3] = ya.domain[1];
}
}
@@ -253,25 +261,23 @@ function plotOne(gd, cd0) {
var xpx = stash.xpx = new Array(visibleLength);
var ypx = stash.ypx = new Array(visibleLength);
- for(i = 0, k = 0; i < dimensions.length; i++) {
- if(trace.dimensions[i].visible) {
- xa = AxisIDs.getFromId(gd, trace._diag[i][0]);
- if(xa) {
- xpx[k] = new Array(commonLength);
- for(j = 0; j < commonLength; j++) {
- xpx[k][j] = xa.c2p(cdata[k][j]);
- }
- }
+ for(k = 0; k < visibleDims.length; k++) {
+ i = visibleDims[k];
- ya = AxisIDs.getFromId(gd, trace._diag[i][1]);
- if(ya) {
- ypx[k] = new Array(commonLength);
- for(j = 0; j < commonLength; j++) {
- ypx[k][j] = ya.c2p(cdata[k][j]);
- }
+ xa = AxisIDs.getFromId(gd, trace._diag[i][0]);
+ if(xa) {
+ xpx[k] = new Array(commonLength);
+ for(j = 0; j < commonLength; j++) {
+ xpx[k][j] = xa.c2p(cdata[k][j]);
}
+ }
- k++;
+ ya = AxisIDs.getFromId(gd, trace._diag[i][1]);
+ if(ya) {
+ ypx[k] = new Array(commonLength);
+ for(j = 0; j < commonLength; j++) {
+ ypx[k][j] = ya.c2p(cdata[k][j]);
+ }
}
}
@@ -286,8 +292,8 @@ function plotOne(gd, cd0) {
}
}
else {
- scene.matrix.update(matrixOpts);
- scene.matrix.update(viewOpts);
+ scene.matrix.update(matrixOpts, null);
+ scene.matrix.update(viewOpts, null);
stash.xpx = stash.ypx = null;
}
@@ -306,8 +312,8 @@ function hoverPoints(pointData, xval, yval) {
var ypx = ya.c2p(yval);
var maxDistance = pointData.distance;
- var xi = getDimIndex(trace, xa);
- var yi = getDimIndex(trace, ya);
+ var xi = getDimIndex(trace, stash, xa);
+ var yi = getDimIndex(trace, stash, ya);
if(xi === false || yi === false) return [pointData];
var x = cdata[xi];
@@ -356,8 +362,8 @@ function selectPoints(searchInfo, polygon) {
var hasOnlyLines = (!subTypes.hasMarkers(trace) && !subTypes.hasText(trace));
if(trace.visible !== true || hasOnlyLines) return selection;
- var xi = getDimIndex(trace, xa);
- var yi = getDimIndex(trace, ya);
+ var xi = getDimIndex(trace, stash, xa);
+ var yi = getDimIndex(trace, stash, ya);
if(xi === false || yi === false) return selection;
var xpx = stash.xpx[xi];
@@ -438,17 +444,15 @@ function style(gd, cds) {
}
}
-function getDimIndex(trace, ax) {
+function getDimIndex(trace, stash, ax) {
var axId = ax._id;
var axLetter = axId.charAt(0);
var ind = {x: 0, y: 1}[axLetter];
- var dimensions = trace.dimensions;
+ var visibleDims = stash.visibleDims;
- for(var i = 0, k = 0; i < dimensions.length; i++) {
- if(dimensions[i].visible) {
- if(trace._diag[i][ind] === axId) return k;
- k++;
- }
+ for(var k = 0; k < visibleDims.length; k++) {
+ var i = visibleDims[k];
+ if(trace._diag[i][ind] === axId) return k;
}
return false;
}
diff --git a/test/image/baselines/splom_mismatched-axis-types.png b/test/image/baselines/splom_mismatched-axis-types.png
new file mode 100644
index 00000000000..cb8d5793a81
Binary files /dev/null and b/test/image/baselines/splom_mismatched-axis-types.png differ
diff --git a/test/image/baselines/splom_multi-axis-type.png b/test/image/baselines/splom_multi-axis-type.png
new file mode 100644
index 00000000000..13c9aa9b176
Binary files /dev/null and b/test/image/baselines/splom_multi-axis-type.png differ
diff --git a/test/image/mocks/splom_mismatched-axis-types.json b/test/image/mocks/splom_mismatched-axis-types.json
new file mode 100644
index 00000000000..bddc0519b0a
--- /dev/null
+++ b/test/image/mocks/splom_mismatched-axis-types.json
@@ -0,0 +1,52 @@
+{
+ "data": [
+ {
+ "type": "splom",
+ "showupperhalf": false,
+ "diagonal": {"visible": false },
+ "dimensions": [
+ {
+ "label": "numeric",
+ "values": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
+ },
+ {
+ "label": "decimal",
+ "values": [-3.3, 2.2, -1.1, 0, 1.1, -2.2, 3.3, 4.4, -5, 6]
+ },
+ {
+ "label": "bool",
+ "values": [false, true, true, true, false, true, false, false, false, true],
+ "axis": {"type": "category"}
+ },
+ {
+ "label": "0/1",
+ "values": [0, 1, 1, 1, 1, 1, 0, 0, 0, 0],
+ "axis": {"type": "category"}
+ },
+ {
+ "label": "blank",
+ "values": []
+ },
+ {
+ "label": "string",
+ "values": ["lyndon", "richard", "gerald", "jimmy", "ronald", "george", "bill", "georgeW", "barack", "donald"]
+ }
+ ]
+ }
+ ],
+ "layout": {
+ "hovermode": "closest",
+ "margin": {"b": 80, "l": 80, "r": 30, "t": 30},
+ "xaxis3": {"type": "linear"},
+ "annotations": [{
+ "showarrow": false,
+ "xref": "paper", "yref": "paper",
+ "xanchor": "right", "yanchor": "top",
+ "x": 1, "y": 1,
+ "text": "Should not see points in the \"bool\" dimension
as it has conflicting axis types",
+ "borderwidth": 1,
+ "bordercolor": "black",
+ "borderpad": 5
+ }]
+ }
+}
diff --git a/test/image/mocks/splom_multi-axis-type.json b/test/image/mocks/splom_multi-axis-type.json
new file mode 100644
index 00000000000..35a817b0a40
--- /dev/null
+++ b/test/image/mocks/splom_multi-axis-type.json
@@ -0,0 +1,38 @@
+{
+ "data": [
+ {
+ "type": "splom",
+ "opacity": 0.9,
+ "showupperhalf": false,
+ "diagonal": {"visible": false },
+ "dimensions": [
+ {
+ "label": "numeric",
+ "values": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
+ },
+ {
+ "label": "decimal",
+ "values": [-3.3, 2.2, -1.1, 0, 1.1, -2.2, 3.3, 4.4, -5, 6]
+ },
+ {
+ "label": "bool",
+ "values": [false, true, true, true, false, true, false, false, false, true],
+ "axis": {"type": "category"}
+ },
+ {
+ "label": "0/1",
+ "values": [0, 1, 1, 1, 1, 1, 0, 0, 0, 0],
+ "axis": {"type": "category"}
+ },
+ {
+ "label": "string",
+ "values": ["lyndon", "richard", "gerald", "jimmy", "ronald", "george", "bill", "georgeW", "barack", "donald"]
+ }
+ ]
+ }
+ ],
+ "layout": {
+ "hovermode": "closest",
+ "margin": {"b": 80, "l": 80, "r": 30, "t": 30}
+ }
+}
diff --git a/test/jasmine/tests/splom_test.js b/test/jasmine/tests/splom_test.js
index 43e37a3b3c3..0ec0a5de4e2 100644
--- a/test/jasmine/tests/splom_test.js
+++ b/test/jasmine/tests/splom_test.js
@@ -10,6 +10,7 @@ var destroyGraphDiv = require('../assets/destroy_graph_div');
var failTest = require('../assets/fail_test');
var mouseEvent = require('../assets/mouse_event');
var drag = require('../assets/drag');
+var doubleClick = require('../assets/double_click');
var customAssertions = require('../assets/custom_assertions');
var assertHoverLabelContent = customAssertions.assertHoverLabelContent;
@@ -352,8 +353,81 @@ describe('Test splom trace defaults:', function() {
});
var fullLayout = gd._fullLayout;
- expect(fullLayout.xaxis.type).toBe('date');
- expect(fullLayout.yaxis.type).toBe('date');
+ expect(fullLayout.xaxis.type).toBe('linear', 'fallbacks to linear for visible:false traces');
+ expect(fullLayout.yaxis.type).toBe('linear', 'fallbacks to linear for visible:false traces');
+ expect(fullLayout.xaxis2.type).toBe('date');
+ expect(fullLayout.yaxis2.type).toBe('date');
+ });
+
+ it('axis type in layout takes precedence over dimensions setting', function() {
+ _supply({
+ dimensions: [
+ {values: [1, 2, 1], axis: {type: 'category'}},
+ {values: [2, 1, 3]}
+ ]
+ }, {
+ xaxis: {type: 'linear'},
+ yaxis: {type: 'linear'},
+ xaxis2: {type: 'category'},
+ yaxis2: {type: 'category'}
+ });
+
+ var fullLayout = gd._fullLayout;
+ expect(fullLayout.xaxis.type).toBe('linear');
+ expect(fullLayout.yaxis.type).toBe('linear');
+ expect(fullLayout.xaxis2.type).toBe('category');
+ expect(fullLayout.yaxis2.type).toBe('category');
+ });
+
+ it('axis type setting should be skipped when dimension is not visible', function() {
+ _supply({
+ dimensions: [
+ {visible: false, values: [1, 2, 1], axis: {type: 'category'}},
+ {values: [-1, 2, 3], axis: {type: 'category'}},
+ ]
+ }, {
+ });
+
+ var fullLayout = gd._fullLayout;
+ expect(fullLayout.xaxis.type).toBe('linear');
+ expect(fullLayout.yaxis.type).toBe('linear');
+ expect(fullLayout.xaxis2.type).toBe('category');
+ expect(fullLayout.yaxis2.type).toBe('category');
+ });
+});
+
+describe('Test splom trace calc step:', function() {
+ var gd;
+
+ function _calc(opts, layout) {
+ gd = {};
+
+ gd.data = [Lib.extendFlat({type: 'splom'}, opts || {})];
+ gd.layout = layout || {};
+ supplyAllDefaults(gd);
+ Plots.doCalcdata(gd);
+ }
+
+ it('should skip dimensions with conflicting axis types', function() {
+ spyOn(Lib, 'log').and.callThrough();
+
+ _calc({
+ dimensions: [{
+ values: [1, 2, 3]
+ }, {
+ values: [2, 1, 2]
+ }]
+ }, {
+ xaxis: {type: 'category'},
+ yaxis: {type: 'linear'}
+ });
+
+ var cd = gd.calcdata[0][0];
+
+ expect(cd.t._scene.matrixOptions.data).toBeCloseTo2DArray([[2, 1, 2]]);
+ expect(cd.t.visibleDims).toEqual([1]);
+ expect(Lib.log).toHaveBeenCalledTimes(1);
+ expect(Lib.log).toHaveBeenCalledWith('Skipping splom dimension 0 with conflicting axis types');
});
});
@@ -849,7 +923,7 @@ describe('@gl Test splom select:', function() {
}
it('should emit correct event data and draw selection outlines', function(done) {
- var fig = require('@mocks/splom_0.json');
+ var fig = Lib.extendDeep({}, require('@mocks/splom_0.json'));
fig.layout = {
dragmode: 'select',
width: 400,
@@ -970,4 +1044,105 @@ describe('@gl Test splom select:', function() {
.catch(failTest)
.then(done);
});
+
+ it('should behave correctly during select->dblclick->pan scenarios', function(done) {
+ var fig = Lib.extendDeep({}, require('@mocks/splom_0.json'));
+ fig.layout = {
+ width: 400,
+ height: 400,
+ margin: {l: 0, t: 0, r: 0, b: 0},
+ grid: {xgap: 0, ygap: 0}
+ };
+
+ var scene;
+
+ function _assert(msg, exp) {
+ expect(scene.matrix.update).toHaveBeenCalledTimes(exp.updateCnt, 'update cnt');
+ expect(scene.matrix.draw).toHaveBeenCalledTimes(exp.drawCnt, 'draw cnt');
+
+ expect(scene.matrix.traces.length).toBe(exp.matrixTraces, '# of regl-splom traces');
+ expect(scene.selectBatch).toEqual(exp.selectBatch, 'selectBatch');
+ expect(scene.unselectBatch).toEqual(exp.unselectBatch, 'unselectBatch');
+
+ scene.matrix.update.calls.reset();
+ scene.matrix.draw.calls.reset();
+ }
+
+ Plotly.plot(gd, fig).then(function() {
+ scene = gd.calcdata[0][0].t._scene;
+ spyOn(scene.matrix, 'update').and.callThrough();
+ spyOn(scene.matrix, 'draw').and.callThrough();
+ })
+ .then(function() {
+ _assert('base', {
+ updateCnt: 0,
+ drawCnt: 0,
+ matrixTraces: 1,
+ selectBatch: null,
+ unselectBatch: null
+ });
+ })
+ .then(function() { return Plotly.relayout(gd, 'dragmode', 'select'); })
+ .then(function() {
+ _assert('under dragmode:select', {
+ updateCnt: 3, // updates positions, viewport and style in 3 calls
+ drawCnt: 1, // results in a 'plot' edit
+ matrixTraces: 2,
+ selectBatch: [],
+ unselectBatch: []
+ });
+ })
+ .then(function() { return _select([[5, 5], [100, 100]]); })
+ .then(function() {
+ _assert('after selection', {
+ updateCnt: 0,
+ drawCnt: 1,
+ matrixTraces: 2,
+ selectBatch: [1],
+ unselectBatch: [0, 2]
+ });
+ })
+ .then(function() { return Plotly.relayout(gd, 'dragmode', 'pan'); })
+ .then(function() {
+ _assert('under dragmode:pan with active selection', {
+ updateCnt: 0,
+ drawCnt: 0, // nothing here, this is a 'modebar' edit
+ matrixTraces: 2,
+ selectBatch: [1],
+ unselectBatch: [0, 2]
+ });
+ })
+ .then(function() { return Plotly.relayout(gd, 'dragmode', 'select'); })
+ .then(function() {
+ _assert('back dragmode:select', {
+ updateCnt: 3,
+ drawCnt: 1, // a 'plot' edit (again)
+ matrixTraces: 2,
+ selectBatch: [1],
+ unselectBatch: [0, 2]
+ });
+ })
+ .then(function() { return doubleClick(100, 100); })
+ .then(function() {
+ _assert('after dblclick clearing selection', {
+ updateCnt: 0,
+ drawCnt: 1,
+ matrixTraces: 2,
+ selectBatch: null,
+ unselectBatch: []
+ });
+ })
+ .then(function() { return Plotly.relayout(gd, 'dragmode', 'pan'); })
+ .then(function() {
+ _assert('under dragmode:pan with NO active selection', {
+ updateCnt: 1, // to clear off 1 matrixTrace
+ drawCnt: 0,
+ matrixTraces: 1, // N.B. back to '1' here
+ selectBatch: null,
+ unselectBatch: []
+ });
+ })
+ .catch(failTest)
+ .then(done);
+ });
});