@@ -25,14 +25,15 @@ var formatColor = require('../../lib/gl_format_color');
2525
2626var subTypes = require ( '../scatter/subtypes' ) ;
2727var calcMarkerSize = require ( '../scatter/calc' ) . calcMarkerSize ;
28+ var calcAxisExpansion = require ( '../scatter/calc' ) . calcAxisExpansion ;
2829var calcColorscales = require ( '../scatter/colorscale_calc' ) ;
2930var makeBubbleSizeFn = require ( '../scatter/make_bubble_size_func' ) ;
3031var linkTraces = require ( '../scatter/link_traces' ) ;
3132var getTraceColor = require ( '../scatter/get_trace_color' ) ;
3233var fillHoverText = require ( '../scatter/fill_hover_text' ) ;
33- var isNumeric = require ( 'fast-isnumeric' ) ;
3434
3535var DASHES = require ( '../../constants/gl2d_dashes' ) ;
36+ var BADNUM = require ( '../../constants/numerical' ) . BADNUM ;
3637var SYMBOL_SDF_SIZE = 200 ;
3738var SYMBOL_SIZE = 20 ;
3839var 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
190167function 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
11571140function 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