@@ -25,14 +25,15 @@ var formatColor = require('../../lib/gl_format_color');
25
25
26
26
var subTypes = require ( '../scatter/subtypes' ) ;
27
27
var calcMarkerSize = require ( '../scatter/calc' ) . calcMarkerSize ;
28
+ var calcAxisExpansion = require ( '../scatter/calc' ) . calcAxisExpansion ;
28
29
var calcColorscales = require ( '../scatter/colorscale_calc' ) ;
29
30
var makeBubbleSizeFn = require ( '../scatter/make_bubble_size_func' ) ;
30
31
var linkTraces = require ( '../scatter/link_traces' ) ;
31
32
var getTraceColor = require ( '../scatter/get_trace_color' ) ;
32
33
var fillHoverText = require ( '../scatter/fill_hover_text' ) ;
33
- var isNumeric = require ( 'fast-isnumeric' ) ;
34
34
35
35
var DASHES = require ( '../../constants/gl2d_dashes' ) ;
36
+ var BADNUM = require ( '../../constants/numerical' ) . BADNUM ;
36
37
var SYMBOL_SDF_SIZE = 200 ;
37
38
var SYMBOL_SIZE = 20 ;
38
39
var SYMBOL_STROKE = SYMBOL_SIZE / 20 ;
@@ -47,116 +48,73 @@ function calc(gd, trace) {
47
48
var xa = Axes . getFromId ( gd , trace . xaxis ) ;
48
49
var ya = Axes . getFromId ( gd , trace . yaxis ) ;
49
50
var subplot = fullLayout . _plots [ trace . xaxis + trace . yaxis ] ;
51
+ var count = trace . _length ;
52
+ var count2 = count * 2 ;
50
53
var stash = { } ;
54
+ var i , xx , yy ;
51
55
56
+ var x = xa . makeCalcdata ( trace , 'x' ) ;
57
+ var y = ya . makeCalcdata ( trace , 'y' ) ;
52
58
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
+ }
57
71
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 ] ) ;
62
75
}
63
76
}
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 ] ) ;
68
80
}
69
81
}
70
82
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
-
94
83
// we don't build a tree for log axes since it takes long to convert log2px
95
84
// and it is also
96
- if ( xaxis . type !== 'log' && yaxis . type !== 'log' ) {
85
+ if ( xa . type !== 'log' && ya . type !== 'log' ) {
97
86
// FIXME: delegate this to webworker
98
87
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 ) ;
102
90
for ( i = 0 ; i < count ; i ++ ) {
103
91
ids [ i ] = i ;
104
92
}
105
93
}
106
94
95
+ // create scene options and scene
107
96
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 ) ) ;
154
113
}
114
+ fastAxisExpand ( xa , x , ppad ) ;
115
+ fastAxisExpand ( ya , y , ppad ) ;
155
116
}
156
117
157
- // create scene
158
- var scene = sceneUpdate ( container , subplot ) ;
159
-
160
118
// set flags to create scene renderers
161
119
if ( options . fill && ! scene . fill2d ) scene . fill2d = true ;
162
120
if ( options . marker && ! scene . scatter2d ) scene . scatter2d = true ;
@@ -178,14 +136,33 @@ function calc(gd, trace) {
178
136
stash . index = scene . count - 1 ;
179
137
stash . x = x ;
180
138
stash . y = y ;
181
- stash . rawx = rawx ;
182
- stash . rawy = rawy ;
183
139
stash . positions = positions ;
184
140
stash . count = count ;
185
141
142
+ gd . firstscatter = false ;
186
143
return [ { x : false , y : false , t : stash , trace : trace } ] ;
187
144
}
188
145
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
+
189
166
// create scene options
190
167
function sceneOptions ( gd , subplot , trace , positions ) {
191
168
var fullLayout = gd . _fullLayout ;
@@ -481,11 +458,15 @@ function sceneOptions(gd, subplot, trace, positions) {
481
458
if ( multiSize || multiLineWidth ) {
482
459
var sizes = markerOptions . sizes = new Array ( count ) ;
483
460
var borderSizes = markerOptions . borderSizes = new Array ( count ) ;
461
+ var sizeTotal = 0 ;
462
+ var sizeAvg ;
484
463
485
464
if ( multiSize ) {
486
465
for ( i = 0 ; i < count ; i ++ ) {
487
466
sizes [ i ] = markerSizeFunc ( markerOpts . size [ i ] ) ;
467
+ sizeTotal += sizes [ i ] ;
488
468
}
469
+ sizeAvg = sizeTotal / count ;
489
470
} else {
490
471
s = markerSizeFunc ( markerOpts . size ) ;
491
472
for ( i = 0 ; i < count ; i ++ ) {
@@ -504,6 +485,8 @@ function sceneOptions(gd, subplot, trace, positions) {
504
485
borderSizes [ i ] = s ;
505
486
}
506
487
}
488
+
489
+ markerOptions . sizeAvg = sizeAvg ;
507
490
} else {
508
491
markerOptions . size = markerSizeFunc ( markerOpts && markerOpts . size || 10 ) ;
509
492
markerOptions . borderSizes = markerSizeFunc ( markerOpts . line . width ) ;
@@ -887,8 +870,8 @@ function plot(gd, subplot, cdata) {
887
870
var trace = cd . trace ;
888
871
var stash = cd . t ;
889
872
var id = stash . index ;
890
- var x = stash . rawx ,
891
- y = stash . rawy ;
873
+ var x = stash . x ;
874
+ var y = stash . y ;
892
875
893
876
var xaxis = subplot . xaxis || Axes . getFromId ( gd , trace . xaxis || 'x' ) ;
894
877
var yaxis = subplot . yaxis || Axes . getFromId ( gd , trace . yaxis || 'y' ) ;
@@ -998,8 +981,8 @@ function hoverPoints(pointData, xval, yval, hovermode) {
998
981
var trace = cd [ 0 ] . trace ;
999
982
var xa = pointData . xa ;
1000
983
var ya = pointData . ya ;
1001
- var x = stash . rawx ;
1002
- var y = stash . rawy ;
984
+ var x = stash . x ;
985
+ var y = stash . y ;
1003
986
var xpx = xa . c2p ( xval ) ;
1004
987
var ypx = ya . c2p ( yval ) ;
1005
988
var maxDistance = pointData . distance ;
@@ -1155,15 +1138,12 @@ function hoverPoints(pointData, xval, yval, hovermode) {
1155
1138
}
1156
1139
1157
1140
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 ;
1167
1147
var scene = stash . scene ;
1168
1148
1169
1149
if ( ! scene ) return selection ;
@@ -1183,8 +1163,8 @@ function selectPoints(searchInfo, polygon) {
1183
1163
els . push ( i ) ;
1184
1164
selection . push ( {
1185
1165
pointNumber : i ,
1186
- x : rawx ? rawx [ i ] : x [ i ] ,
1187
- y : rawy ? rawy [ i ] : y [ i ]
1166
+ x : x [ i ] ,
1167
+ y : y [ i ]
1188
1168
} ) ;
1189
1169
}
1190
1170
else {
0 commit comments