Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 0aa0f5e

Browse filesBrowse files
committed
improvements to Scattergl.calc
- reuse scatter axis-expansion logic - improve 'fast' axis expand routine (using average marker.size as pad value) - use ax.makeCalcdata for all axis types (this creates a new array for linear axes, but makes thing more robust) - add a few TODOs
1 parent 5316c47 commit 0aa0f5e
Copy full SHA for 0aa0f5e

File tree

Expand file treeCollapse file tree

3 files changed

+124
-133
lines changed
Filter options
Expand file treeCollapse file tree

3 files changed

+124
-133
lines changed

‎src/plots/cartesian/axes.js

Copy file name to clipboardExpand all lines: src/plots/cartesian/axes.js
+8-6Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -423,6 +423,13 @@ axes.saveShowSpikeInitial = function(gd, overwrite) {
423423
return hasOneAxisChanged;
424424
};
425425

426+
axes.doesAxisNeedAutoRange = function(ax) {
427+
return (
428+
ax.autorange ||
429+
!!Lib.nestedProperty(ax, 'rangeslider.autorange').get()
430+
);
431+
};
432+
426433
// axes.expand: if autoranging, include new data in the outer limits
427434
// for this axis
428435
// data is an array of numbers (ie already run through ax.d2c)
@@ -436,12 +443,7 @@ axes.saveShowSpikeInitial = function(gd, overwrite) {
436443
// tozero: (boolean) make sure to include zero if axis is linear,
437444
// and make it a tight bound if possible
438445
axes.expand = function(ax, data, options) {
439-
var needsAutorange = (
440-
ax.autorange ||
441-
!!Lib.nestedProperty(ax, 'rangeslider.autorange').get()
442-
);
443-
444-
if(!needsAutorange || !data) return;
446+
if(!axes.doesAxisNeedAutoRange(ax) || !data) return;
445447

446448
if(!ax._min) ax._min = [];
447449
if(!ax._max) ax._max = [];

‎src/traces/scatter/calc.js

Copy file name to clipboardExpand all lines: src/traces/scatter/calc.js
+30-21Lines changed: 30 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,31 @@ function calc(gd, trace) {
2525
var x = xa.makeCalcdata(trace, 'x');
2626
var y = ya.makeCalcdata(trace, 'y');
2727
var serieslen = trace._length;
28+
var cd = new Array(serieslen);
29+
30+
var ppad = calcMarkerSize(trace, serieslen);
31+
calcAxisExpansion(gd, trace, xa, ya, x, y, ppad);
32+
33+
for(var i = 0; i < serieslen; i++) {
34+
cd[i] = (isNumeric(x[i]) && isNumeric(y[i])) ?
35+
{x: x[i], y: y[i]} :
36+
{x: BADNUM, y: BADNUM};
37+
38+
if(trace.ids) {
39+
cd[i].id = String(trace.ids[i]);
40+
}
41+
}
42+
43+
arraysToCalcdata(cd, trace);
44+
calcColorscale(trace);
45+
calcSelection(cd, trace);
46+
47+
gd.firstscatter = false;
48+
return cd;
49+
}
50+
51+
function calcAxisExpansion(gd, trace, xa, ya, x, y, ppad) {
52+
var serieslen = trace._length;
2853

2954
// cancel minimum tick spacings (only applies to bars and boxes)
3055
xa._minDtick = 0;
@@ -35,8 +60,9 @@ function calc(gd, trace) {
3560
var xOptions = {padded: true};
3661
var yOptions = {padded: true};
3762

38-
var ppad = calcMarkerSize(trace, serieslen);
39-
if(ppad) xOptions.ppad = yOptions.ppad = ppad;
63+
if(ppad) {
64+
xOptions.ppad = yOptions.ppad = ppad;
65+
}
4066

4167
// TODO: text size
4268

@@ -72,24 +98,6 @@ function calc(gd, trace) {
7298

7399
Axes.expand(xa, x, xOptions);
74100
Axes.expand(ya, y, yOptions);
75-
76-
// create the "calculated data" to plot
77-
var cd = new Array(serieslen);
78-
for(var i = 0; i < serieslen; i++) {
79-
cd[i] = (isNumeric(x[i]) && isNumeric(y[i])) ?
80-
{x: x[i], y: y[i]} : {x: BADNUM, y: BADNUM};
81-
82-
if(trace.ids) {
83-
cd[i].id = String(trace.ids[i]);
84-
}
85-
}
86-
87-
arraysToCalcdata(cd, trace);
88-
calcColorscale(trace);
89-
calcSelection(cd, trace);
90-
91-
gd.firstscatter = false;
92-
return cd;
93101
}
94102

95103
function calcMarkerSize(trace, serieslen) {
@@ -131,5 +139,6 @@ function calcMarkerSize(trace, serieslen) {
131139

132140
module.exports = {
133141
calc: calc,
134-
calcMarkerSize: calcMarkerSize
142+
calcMarkerSize: calcMarkerSize,
143+
calcAxisExpansion: calcAxisExpansion
135144
};

‎src/traces/scattergl/index.js

Copy file name to clipboardExpand all lines: src/traces/scattergl/index.js
+86-106Lines changed: 86 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,15 @@ var formatColor = require('../../lib/gl_format_color');
2525

2626
var subTypes = require('../scatter/subtypes');
2727
var calcMarkerSize = require('../scatter/calc').calcMarkerSize;
28+
var calcAxisExpansion = require('../scatter/calc').calcAxisExpansion;
2829
var calcColorscales = require('../scatter/colorscale_calc');
2930
var makeBubbleSizeFn = require('../scatter/make_bubble_size_func');
3031
var linkTraces = require('../scatter/link_traces');
3132
var getTraceColor = require('../scatter/get_trace_color');
3233
var fillHoverText = require('../scatter/fill_hover_text');
33-
var isNumeric = require('fast-isnumeric');
3434

3535
var DASHES = require('../../constants/gl2d_dashes');
36+
var BADNUM = require('../../constants/numerical').BADNUM;
3637
var SYMBOL_SDF_SIZE = 200;
3738
var SYMBOL_SIZE = 20;
3839
var SYMBOL_STROKE = SYMBOL_SIZE / 20;
@@ -47,116 +48,73 @@ function calc(gd, trace) {
4748
var xa = Axes.getFromId(gd, trace.xaxis);
4849
var ya = Axes.getFromId(gd, trace.yaxis);
4950
var subplot = fullLayout._plots[trace.xaxis + trace.yaxis];
51+
var count = trace._length;
52+
var count2 = count * 2;
5053
var stash = {};
54+
var i, xx, yy;
5155

56+
var x = xa.makeCalcdata(trace, 'x');
57+
var y = ya.makeCalcdata(trace, 'y');
5258

53-
var x = xaxis.type === 'linear' ? trace.x : xaxis.makeCalcdata(trace, 'x');
54-
var y = yaxis.type === 'linear' ? trace.y : yaxis.makeCalcdata(trace, 'y');
55-
56-
var count = trace._length, i, xx, yy;
59+
// we need hi-precision for scatter2d,
60+
// regl-scatter2d uses NaNs for bad/missing values
61+
//
62+
// TODO should this be a Float32Array ??
63+
var positions = new Array(count2);
64+
for(i = 0; i < count; i++) {
65+
xx = x[i];
66+
yy = y[i];
67+
// TODO does d2c output any other bad value as BADNUM ever?
68+
positions[i * 2] = xx === BADNUM ? NaN : xx;
69+
positions[i * 2 + 1] = yy === BADNUM ? NaN : yy;
70+
}
5771

58-
if(!x) {
59-
x = Array(count);
60-
for(i = 0; i < count; i++) {
61-
x[i] = i;
72+
if(xa.type === 'log') {
73+
for(i = 0; i < count2; i += 2) {
74+
positions[i] = xa.d2l(positions[i]);
6275
}
6376
}
64-
if(!y) {
65-
y = Array(count);
66-
for(i = 0; i < count; i++) {
67-
y[i] = i;
77+
if(ya.type === 'log') {
78+
for(i = 1; i < count2; i += 2) {
79+
positions[i] = ya.d2l(positions[i]);
6880
}
6981
}
7082

71-
// get log converted positions
72-
var rawx = (xaxis.type === 'log' || x.length > count) ? x.slice(0, count) : x;
73-
var rawy = (yaxis.type === 'log' || y.length > count) ? y.slice(0, count) : y;
74-
75-
var convertX = (xaxis.type === 'log') ? xaxis.d2l : parseFloat;
76-
var convertY = (yaxis.type === 'log') ? yaxis.d2l : parseFloat;
77-
78-
// we need hi-precision for scatter2d
79-
positions = new Array(count * 2);
80-
81-
for(i = 0; i < count; i++) {
82-
x[i] = convertX(x[i]);
83-
y[i] = convertY(y[i]);
84-
85-
// if no x defined, we are creating simple int sequence (API)
86-
// we use parseFloat because it gives NaN (we need that for empty values to avoid drawing lines) and it is incredibly fast
87-
xx = isNumeric(x[i]) ? +x[i] : NaN;
88-
yy = isNumeric(y[i]) ? +y[i] : NaN;
89-
90-
positions[i * 2] = xx;
91-
positions[i * 2 + 1] = yy;
92-
}
93-
9483
// we don't build a tree for log axes since it takes long to convert log2px
9584
// and it is also
96-
if(xaxis.type !== 'log' && yaxis.type !== 'log') {
85+
if(xa.type !== 'log' && ya.type !== 'log') {
9786
// FIXME: delegate this to webworker
9887
stash.tree = kdtree(positions, 512);
99-
}
100-
else {
101-
var ids = stash.ids = Array(count);
88+
} else {
89+
var ids = stash.ids = new Array(count);
10290
for(i = 0; i < count; i++) {
10391
ids[i] = i;
10492
}
10593
}
10694

95+
// create scene options and scene
10796
calcColorscales(trace);
108-
109-
var options = sceneOptions(container, subplot, trace, positions);
110-
111-
// expanding axes is separate from options
112-
if(!options.markers) {
113-
Axes.expand(xaxis, rawx, { padded: true });
114-
Axes.expand(yaxis, rawy, { padded: true });
115-
}
116-
else if(Lib.isArrayOrTypedArray(options.markers.sizes)) {
117-
var sizes = options.markers.sizes;
118-
Axes.expand(xaxis, rawx, { padded: true, ppad: sizes });
119-
Axes.expand(yaxis, rawy, { padded: true, ppad: sizes });
120-
}
121-
else {
122-
var xbounds = [Infinity, -Infinity], ybounds = [Infinity, -Infinity];
123-
var size = options.markers.size;
124-
125-
// axes bounds
126-
for(i = 0; i < count; i++) {
127-
xx = x[i], yy = y[i];
128-
if(xbounds[0] > xx) xbounds[0] = xx;
129-
if(xbounds[1] < xx) xbounds[1] = xx;
130-
if(ybounds[0] > yy) ybounds[0] = yy;
131-
if(ybounds[1] < yy) ybounds[1] = yy;
132-
}
133-
134-
// FIXME: is there a better way to separate expansion?
135-
if(count < TOO_MANY_POINTS) {
136-
Axes.expand(xaxis, rawx, { padded: true, ppad: size });
137-
Axes.expand(yaxis, rawy, { padded: true, ppad: size });
138-
}
139-
// update axes fast for big number of points
140-
else {
141-
if(xaxis._min) {
142-
xaxis._min.push({ val: xbounds[0], pad: size });
143-
}
144-
if(xaxis._max) {
145-
xaxis._max.push({ val: xbounds[1], pad: size });
146-
}
147-
148-
if(yaxis._min) {
149-
yaxis._min.push({ val: ybounds[0], pad: size });
150-
}
151-
if(yaxis._max) {
152-
yaxis._max.push({ val: ybounds[1], pad: size });
153-
}
97+
var options = sceneOptions(gd, subplot, trace, positions);
98+
var markerOptions = options.marker;
99+
var scene = sceneUpdate(gd, subplot);
100+
var ppad;
101+
102+
// Re-use SVG scatter axis expansion routine except
103+
// for graph with very large number of points where it
104+
// performs poorly.
105+
// In big data case, fake Axes.expand outputs with data bounds,
106+
// and an average size for array marker.size inputs.
107+
if(count < TOO_MANY_POINTS) {
108+
ppad = calcMarkerSize(trace, count);
109+
calcAxisExpansion(gd, trace, xa, ya, x, y, ppad);
110+
} else {
111+
if(markerOptions) {
112+
ppad = 2 * (markerOptions.sizeAvg || Math.max(markerOptions.size, 3));
154113
}
114+
fastAxisExpand(xa, x, ppad);
115+
fastAxisExpand(ya, y, ppad);
155116
}
156117

157-
// create scene
158-
var scene = sceneUpdate(container, subplot);
159-
160118
// set flags to create scene renderers
161119
if(options.fill && !scene.fill2d) scene.fill2d = true;
162120
if(options.marker && !scene.scatter2d) scene.scatter2d = true;
@@ -178,14 +136,33 @@ function calc(gd, trace) {
178136
stash.index = scene.count - 1;
179137
stash.x = x;
180138
stash.y = y;
181-
stash.rawx = rawx;
182-
stash.rawy = rawy;
183139
stash.positions = positions;
184140
stash.count = count;
185141

142+
gd.firstscatter = false;
186143
return [{x: false, y: false, t: stash, trace: trace}];
187144
}
188145

146+
// Approximate Axes.expand results with speed
147+
function fastAxisExpand(ax, vals, ppad) {
148+
if(!Axes.doesAxisNeedAutoRange(ax) || !vals) return;
149+
150+
var b0 = Infinity;
151+
var b1 = -Infinity;
152+
153+
for(var i = 0; i < vals.length; i += 2) {
154+
var v = vals[i];
155+
if(v < b0) b0 = v;
156+
if(v > b1) b1 = v;
157+
}
158+
159+
if(ax._min) ax._min = [];
160+
ax._min.push({val: b0, pad: ppad});
161+
162+
if(ax._max) ax._max = [];
163+
ax._max.push({val: b1, pad: ppad});
164+
}
165+
189166
// create scene options
190167
function sceneOptions(gd, subplot, trace, positions) {
191168
var fullLayout = gd._fullLayout;
@@ -481,11 +458,15 @@ function sceneOptions(gd, subplot, trace, positions) {
481458
if(multiSize || multiLineWidth) {
482459
var sizes = markerOptions.sizes = new Array(count);
483460
var borderSizes = markerOptions.borderSizes = new Array(count);
461+
var sizeTotal = 0;
462+
var sizeAvg;
484463

485464
if(multiSize) {
486465
for(i = 0; i < count; i++) {
487466
sizes[i] = markerSizeFunc(markerOpts.size[i]);
467+
sizeTotal += sizes[i];
488468
}
469+
sizeAvg = sizeTotal / count;
489470
} else {
490471
s = markerSizeFunc(markerOpts.size);
491472
for(i = 0; i < count; i++) {
@@ -504,6 +485,8 @@ function sceneOptions(gd, subplot, trace, positions) {
504485
borderSizes[i] = s;
505486
}
506487
}
488+
489+
markerOptions.sizeAvg = sizeAvg;
507490
} else {
508491
markerOptions.size = markerSizeFunc(markerOpts && markerOpts.size || 10);
509492
markerOptions.borderSizes = markerSizeFunc(markerOpts.line.width);
@@ -887,8 +870,8 @@ function plot(gd, subplot, cdata) {
887870
var trace = cd.trace;
888871
var stash = cd.t;
889872
var id = stash.index;
890-
var x = stash.rawx,
891-
y = stash.rawy;
873+
var x = stash.x;
874+
var y = stash.y;
892875

893876
var xaxis = subplot.xaxis || Axes.getFromId(gd, trace.xaxis || 'x');
894877
var yaxis = subplot.yaxis || Axes.getFromId(gd, trace.yaxis || 'y');
@@ -998,8 +981,8 @@ function hoverPoints(pointData, xval, yval, hovermode) {
998981
var trace = cd[0].trace;
999982
var xa = pointData.xa;
1000983
var ya = pointData.ya;
1001-
var x = stash.rawx;
1002-
var y = stash.rawy;
984+
var x = stash.x;
985+
var y = stash.y;
1003986
var xpx = xa.c2p(xval);
1004987
var ypx = ya.c2p(yval);
1005988
var maxDistance = pointData.distance;
@@ -1155,15 +1138,12 @@ function hoverPoints(pointData, xval, yval, hovermode) {
11551138
}
11561139

11571140
function selectPoints(searchInfo, polygon) {
1158-
var cd = searchInfo.cd,
1159-
selection = [],
1160-
trace = cd[0].trace,
1161-
stash = cd[0].t,
1162-
x = stash.x,
1163-
y = stash.y,
1164-
rawx = stash.rawx,
1165-
rawy = stash.rawy;
1166-
1141+
var cd = searchInfo.cd;
1142+
var selection = [];
1143+
var trace = cd[0].trace;
1144+
var stash = cd[0].t;
1145+
var x = stash.x;
1146+
var y = stash.y;
11671147
var scene = stash.scene;
11681148

11691149
if(!scene) return selection;
@@ -1183,8 +1163,8 @@ function selectPoints(searchInfo, polygon) {
11831163
els.push(i);
11841164
selection.push({
11851165
pointNumber: i,
1186-
x: rawx ? rawx[i] : x[i],
1187-
y: rawy ? rawy[i] : y[i]
1166+
x: x[i],
1167+
y: y[i]
11881168
});
11891169
}
11901170
else {

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.