Skip to content

Commit 59588cc

Browse files
committed
first cut splom plot
- only markers for now, reuse scattergl convert logic for markers - use a splom basePlotModule to handle multiple traces and splom-wide field (like regl-line2d grids coming up)
1 parent 76c64e5 commit 59588cc

File tree

5 files changed

+293
-1
lines changed

5 files changed

+293
-1
lines changed

src/traces/scatter/calc.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ function calcAxisExpansion(gd, trace, xa, ya, x, y, ppad) {
7575
}
7676

7777
// if no error bars, markers or text, or fill to y=0 remove x padding
78-
else if(!trace.error_y.visible && (
78+
else if(!(trace.error_y || {}).visible && (
7979
['tonexty', 'tozeroy'].indexOf(trace.fill) !== -1 ||
8080
(!subTypes.hasMarkers(trace) && !subTypes.hasText(trace))
8181
)) {

src/traces/scattergl/convert.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,7 @@ function convertErrorBarPositions(gd, trace, positions) {
401401

402402
module.exports = {
403403
convertStyle: convertStyle,
404+
convertMarkerStyle: convertMarkerStyle,
404405
convertLinePositions: convertLinePositions,
405406
convertErrorBarPositions: convertErrorBarPositions
406407
};

src/traces/splom/base_plot.js

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/**
2+
* Copyright 2012-2018, Plotly, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the MIT license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
9+
'use strict';
10+
11+
var createRegl = require('regl');
12+
13+
var Registry = require('../../registry');
14+
var getModuleCalcData = require('../../plots/get_data').getModuleCalcData;
15+
var Cartesian = require('../../plots/cartesian');
16+
17+
var SPLOM = 'splom';
18+
19+
function plot(gd) {
20+
var fullLayout = gd._fullLayout;
21+
var _module = Registry.getModule(SPLOM);
22+
var splomCalcData = getModuleCalcData(gd.calcdata, _module);
23+
24+
// clear gl frame, if any, since we preserve drawing buffer
25+
if(fullLayout._glcanvas && fullLayout._glcanvas.size()) {
26+
fullLayout._glcanvas.each(function(d) {
27+
if(d.regl) d.regl.clear({color: true});
28+
});
29+
}
30+
31+
// make sure proper regl instances are created
32+
fullLayout._glcanvas.each(function(d) {
33+
if(d.regl || d.pick) return;
34+
d.regl = createRegl({
35+
canvas: this,
36+
attributes: {
37+
antialias: !d.pick,
38+
preserveDrawingBuffer: true
39+
},
40+
extensions: ['ANGLE_instanced_arrays', 'OES_element_index_uint'],
41+
pixelRatio: gd._context.plotGlPixelRatio || global.devicePixelRatio
42+
});
43+
});
44+
45+
_module.plot(gd, {}, splomCalcData);
46+
}
47+
48+
function clean(newFullData, newFullLayout, oldFullData, oldFullLayout) {
49+
// TODO clear regl-splom instances
50+
// TODO clear regl-line2d grid instance!
51+
52+
Cartesian.clean(newFullData, newFullLayout, oldFullData, oldFullLayout);
53+
}
54+
55+
module.exports = {
56+
name: SPLOM,
57+
attrRegex: Cartesian.attrRegex,
58+
layoutAttributes: Cartesian.layoutAttributes,
59+
supplyLayoutDefaults: Cartesian.supplyLayoutDefaults,
60+
drawFramework: Cartesian.drawFramework,
61+
plot: plot,
62+
clean: clean,
63+
toSVG: Cartesian.toSVG
64+
};

src/traces/splom/defaults.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout
3131
coerce('mode', traceOut._commonLength < PTS_LINESONLY ? 'lines+markers' : 'lines');
3232
coerce('text');
3333

34+
// TODO just markers for now
35+
traceOut.mode = 'markers';
36+
3437
if(subTypes.hasLines(traceOut)) {
3538
handleLineDefaults(traceIn, traceOut, defaultColor, layout, coerce);
3639
coerce('connectgaps');
@@ -44,11 +47,13 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout
4447
coerce('marker.line.width', isOpen || isBubble ? 1 : 0);
4548
}
4649

50+
// TODO not implemented yet
4751
coerce('xdirection');
4852
coerce('ydirection');
4953

5054
handleAxisDefaults(traceIn, traceOut, layout, coerce);
5155

56+
// TODO not implemented yet
5257
coerce('showdiagonal');
5358
coerce('showupperhalf');
5459
coerce('showlowerhalf');
@@ -115,6 +120,8 @@ function handleAxisDefaults(traceIn, traceOut, layout, coerce) {
115120
var xaxes = coerce('xaxes', xaxesDflt);
116121
var yaxes = coerce('yaxes', yaxesDflt);
117122

123+
// TODO what to do when xaxes.length or yaxes.length !== dimLength ???
124+
118125
for(i = 0; i < dimLength; i++) {
119126
var dim = dimensions[i];
120127
var xa = xaxes[i];

src/traces/splom/index.js

Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
/**
2+
* Copyright 2012-2018, Plotly, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the MIT license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
9+
'use strict';
10+
11+
var createMatrix = require('regl-scattermatrix');
12+
13+
var Lib = require('../../lib');
14+
var AxisIDs = require('../../plots/cartesian/axis_ids');
15+
16+
var calcMarkerSize = require('../scatter/calc').calcMarkerSize;
17+
var calcAxisExpansion = require('../scatter/calc').calcAxisExpansion;
18+
var calcColorscales = require('../scatter/colorscale_calc');
19+
var convertMarkerStyle = require('../scattergl/convert').convertMarkerStyle;
20+
21+
var BADNUM = require('../../constants/numerical').BADNUM;
22+
var TOO_MANY_POINTS = require('../scattergl/constants').TOO_MANY_POINTS;
23+
24+
function calc(gd, trace) {
25+
var stash = {};
26+
var opts = {};
27+
var i, xa, ya;
28+
29+
var dimLength = trace.dimensions.length;
30+
var hasTooManyPoints = (dimLength * trace._commonLength) > TOO_MANY_POINTS;
31+
var matrix = opts.data = new Array(dimLength);
32+
33+
for(i = 0; i < dimLength; i++) {
34+
// using xa or ya should make no difference here
35+
xa = AxisIDs.getFromId(gd, trace.xaxes[i]);
36+
matrix[i] = makeCalcdata(xa, trace, trace.dimensions[i]);
37+
}
38+
39+
calcColorscales(trace);
40+
Lib.extendFlat(opts, convertMarkerStyle(trace));
41+
42+
for(i = 0; i < dimLength; i++) {
43+
xa = AxisIDs.getFromId(gd, trace.xaxes[i]);
44+
ya = AxisIDs.getFromId(gd, trace.yaxes[i]);
45+
46+
// Re-use SVG scatter axis expansion routine except
47+
// for graph with very large number of points where it
48+
// performs poorly.
49+
// In big data case, fake Axes.expand outputs with data bounds,
50+
// and an average size for array marker.size inputs.
51+
var ppad;
52+
if(hasTooManyPoints) {
53+
ppad = 2 * (opts.sizeAvg || Math.max(opts.size, 3));
54+
} else {
55+
ppad = calcMarkerSize(trace, trace._commonLength);
56+
}
57+
calcAxisExpansion(gd, trace, xa, ya, matrix[i], matrix[i], ppad);
58+
}
59+
60+
61+
var scene = stash.scene = sceneUpdate(gd, stash);
62+
if(!scene.matrix) scene.matrix = true;
63+
scene.matrixOptions = opts;
64+
65+
return [{x: false, y: false, t: stash, trace: trace}];
66+
}
67+
68+
function makeCalcdata(ax, trace, dim) {
69+
var i;
70+
71+
var cdata = ax.makeCalcdata({
72+
v: dim.values,
73+
vcalendar: trace.calendar
74+
}, 'v');
75+
76+
for(i = 0; i < cdata.length; i++) {
77+
cdata[i] = cdata[i] === BADNUM ? NaN : cdata[i];
78+
}
79+
80+
if(ax.type === 'log') {
81+
for(i = 0; i < cdata.length; i++) {
82+
cdata[i] = ax.c2l(cdata[i]);
83+
}
84+
}
85+
86+
return cdata;
87+
}
88+
89+
// TODO do we need this?
90+
function sceneUpdate(gd, stash) {
91+
var scene = stash._scene;
92+
93+
var reset = {
94+
dirty: true,
95+
opts: null
96+
};
97+
98+
var first = {
99+
selectBatch: null,
100+
unselectBatch: null,
101+
matrix: false,
102+
select: null
103+
};
104+
105+
if(!scene) {
106+
scene = stash._scene = Lib.extendFlat({}, reset, first);
107+
108+
// TODO should we use something like this on drag?
109+
scene.update = function update(opt) {
110+
if(scene.matrix) scene.matrix.update(opt);
111+
scene.draw();
112+
};
113+
114+
scene.draw = function draw() {
115+
if(scene.matrix) scene.matrix.draw();
116+
117+
// TODO selection stuff
118+
119+
// do we need to use this flag anywhere??
120+
scene.dirty = false;
121+
};
122+
123+
// make sure canvas is clear
124+
scene.clear = function clear() {
125+
// TODO
126+
};
127+
128+
// remove selection
129+
scene.clearSelect = function clearSelect() {
130+
if(!scene.selectBatch) return;
131+
scene.selectBatch = null;
132+
scene.unselectBatch = null;
133+
scene.matrix.update(scene.opts);
134+
scene.clear();
135+
scene.draw();
136+
};
137+
138+
// remove scene resources
139+
scene.destroy = function destroy() {
140+
if(scene.matrix) scene.matrix.destroy();
141+
142+
scene.opts = null;
143+
scene.selectBatch = null;
144+
scene.unselectBatch = null;
145+
146+
stash._scene = null;
147+
};
148+
}
149+
150+
// In case if we have scene from the last calc - reset data
151+
if(!scene.dirty) {
152+
Lib.extendFlat(scene, reset);
153+
}
154+
155+
return scene;
156+
}
157+
158+
function plot(gd, _, cdata) {
159+
if(!cdata.length) return;
160+
161+
for(var i = 0; i < cdata.length; i++) {
162+
plotOne(gd, cdata[i][0]);
163+
}
164+
}
165+
166+
function plotOne(gd, cd0) {
167+
var fullLayout = gd._fullLayout;
168+
var gs = fullLayout._size;
169+
var scene = cd0.t.scene;
170+
var trace = cd0.trace;
171+
var regl = fullLayout._glcanvas.data()[0].regl;
172+
173+
var dimLength = trace.dimensions.length;
174+
var viewOpts = {
175+
ranges: new Array(dimLength),
176+
domains: new Array(dimLength)
177+
};
178+
179+
for(var i = 0; i < dimLength; i++) {
180+
var xa = AxisIDs.getFromId(gd, trace.xaxes[i]);
181+
var ya = AxisIDs.getFromId(gd, trace.yaxes[i]);
182+
viewOpts.ranges[i] = [xa.range[0], ya.range[0], xa.range[1], ya.range[1]];
183+
viewOpts.domains[i] = [xa.domain[0], ya.domain[0], xa.domain[1], ya.domain[1]];
184+
}
185+
186+
viewOpts.viewport = [gs.l, gs.b, fullLayout.width - gs.r, fullLayout.height - gs.t];
187+
188+
if(scene.matrix === true) {
189+
scene.matrix = createMatrix(regl);
190+
}
191+
192+
scene.matrix.update(scene.matrixOptions);
193+
scene.matrix.update(viewOpts);
194+
scene.matrix.draw();
195+
}
196+
197+
// TODO splom 'needs' the grid component, register it here?
198+
199+
module.exports = {
200+
moduleType: 'trace',
201+
name: 'splom',
202+
203+
basePlotModule: require('./base_plot'),
204+
categories: ['gl', 'regl', 'cartesian', 'symbols', 'markerColorscale', 'showLegend', 'scatter-like'],
205+
206+
attributes: require('./attributes'),
207+
supplyDefaults: require('./defaults'),
208+
209+
calc: calc,
210+
plot: plot,
211+
hoverPoints: function() {},
212+
selectPoints: function() {},
213+
style: function() {},
214+
215+
meta: {
216+
description: [
217+
'SPLOM !!!'
218+
].join(' ')
219+
}
220+
};

0 commit comments

Comments
 (0)