@@ -21,6 +21,7 @@ var Color = require('../../components/color');
21
21
var Drawing = require ( '../../components/drawing' ) ;
22
22
23
23
var axAttrs = require ( './layout_attributes' ) ;
24
+ var cleanTicks = require ( './clean_ticks' ) ;
24
25
25
26
var constants = require ( '../../constants/numerical' ) ;
26
27
var ONEAVGYEAR = constants . ONEAVGYEAR ;
@@ -280,43 +281,22 @@ axes.saveShowSpikeInitial = function(gd, overwrite) {
280
281
return hasOneAxisChanged ;
281
282
} ;
282
283
283
- axes . autoBin = function ( data , ax , nbins , is2d , calendar ) {
284
- var dataMin = Lib . aggNums ( Math . min , null , data ) ,
285
- dataMax = Lib . aggNums ( Math . max , null , data ) ;
286
-
287
- if ( ! calendar ) calendar = ax . calendar ;
284
+ axes . autoBin = function ( data , ax , nbins , is2d , calendar , size ) {
285
+ var dataMin = Lib . aggNums ( Math . min , null , data ) ;
286
+ var dataMax = Lib . aggNums ( Math . max , null , data ) ;
288
287
289
288
if ( ax . type === 'category' ) {
290
289
return {
291
290
start : dataMin - 0.5 ,
292
291
end : dataMax + 0.5 ,
293
- size : 1 ,
292
+ size : Math . max ( 1 , Math . round ( size ) || 1 ) ,
294
293
_dataSpan : dataMax - dataMin ,
295
294
} ;
296
295
}
297
296
298
- var size0 ;
299
- if ( nbins ) size0 = ( ( dataMax - dataMin ) / nbins ) ;
300
- else {
301
- // totally auto: scale off std deviation so the highest bin is
302
- // somewhat taller than the total number of bins, but don't let
303
- // the size get smaller than the 'nice' rounded down minimum
304
- // difference between values
305
- var distinctData = Lib . distinctVals ( data ) ,
306
- msexp = Math . pow ( 10 , Math . floor (
307
- Math . log ( distinctData . minDiff ) / Math . LN10 ) ) ,
308
- minSize = msexp * Lib . roundUp (
309
- distinctData . minDiff / msexp , [ 0.9 , 1.9 , 4.9 , 9.9 ] , true ) ;
310
- size0 = Math . max ( minSize , 2 * Lib . stdev ( data ) /
311
- Math . pow ( data . length , is2d ? 0.25 : 0.4 ) ) ;
312
-
313
- // fallback if ax.d2c output BADNUMs
314
- // e.g. when user try to plot categorical bins
315
- // on a layout.xaxis.type: 'linear'
316
- if ( ! isNumeric ( size0 ) ) size0 = 1 ;
317
- }
297
+ if ( ! calendar ) calendar = ax . calendar ;
318
298
319
- // piggyback off autotick code to make "nice" bin sizes
299
+ // piggyback off tick code to make "nice" bin sizes and edges
320
300
var dummyAx ;
321
301
if ( ax . type === 'log' ) {
322
302
dummyAx = {
@@ -333,19 +313,51 @@ axes.autoBin = function(data, ax, nbins, is2d, calendar) {
333
313
}
334
314
axes . setConvert ( dummyAx ) ;
335
315
336
- axes . autoTicks ( dummyAx , size0 ) ;
316
+ size = size && cleanTicks . dtick ( size , dummyAx . type ) ;
317
+
318
+ if ( size ) {
319
+ dummyAx . dtick = size ;
320
+ dummyAx . tick0 = cleanTicks . tick0 ( undefined , dummyAx . type , calendar ) ;
321
+ }
322
+ else {
323
+ var size0 ;
324
+ if ( nbins ) size0 = ( ( dataMax - dataMin ) / nbins ) ;
325
+ else {
326
+ // totally auto: scale off std deviation so the highest bin is
327
+ // somewhat taller than the total number of bins, but don't let
328
+ // the size get smaller than the 'nice' rounded down minimum
329
+ // difference between values
330
+ var distinctData = Lib . distinctVals ( data ) ;
331
+ var msexp = Math . pow ( 10 , Math . floor (
332
+ Math . log ( distinctData . minDiff ) / Math . LN10 ) ) ;
333
+ var minSize = msexp * Lib . roundUp (
334
+ distinctData . minDiff / msexp , [ 0.9 , 1.9 , 4.9 , 9.9 ] , true ) ;
335
+ size0 = Math . max ( minSize , 2 * Lib . stdev ( data ) /
336
+ Math . pow ( data . length , is2d ? 0.25 : 0.4 ) ) ;
337
+
338
+ // fallback if ax.d2c output BADNUMs
339
+ // e.g. when user try to plot categorical bins
340
+ // on a layout.xaxis.type: 'linear'
341
+ if ( ! isNumeric ( size0 ) ) size0 = 1 ;
342
+ }
343
+
344
+ axes . autoTicks ( dummyAx , size0 ) ;
345
+ }
346
+
347
+
348
+ var finalSize = dummyAx . dtick ;
337
349
var binStart = axes . tickIncrement (
338
- axes . tickFirst ( dummyAx ) , dummyAx . dtick , 'reverse' , calendar ) ;
350
+ axes . tickFirst ( dummyAx ) , finalSize , 'reverse' , calendar ) ;
339
351
var binEnd , bincount ;
340
352
341
353
// check for too many data points right at the edges of bins
342
354
// (>50% within 1% of bin edges) or all data points integral
343
355
// and offset the bins accordingly
344
- if ( typeof dummyAx . dtick === 'number' ) {
356
+ if ( typeof finalSize === 'number' ) {
345
357
binStart = autoShiftNumericBins ( binStart , data , dummyAx , dataMin , dataMax ) ;
346
358
347
- bincount = 1 + Math . floor ( ( dataMax - binStart ) / dummyAx . dtick ) ;
348
- binEnd = binStart + bincount * dummyAx . dtick ;
359
+ bincount = 1 + Math . floor ( ( dataMax - binStart ) / finalSize ) ;
360
+ binEnd = binStart + bincount * finalSize ;
349
361
}
350
362
else {
351
363
// month ticks - should be the only nonlinear kind we have at this point.
@@ -354,23 +366,23 @@ axes.autoBin = function(data, ax, nbins, is2d, calendar) {
354
366
// we bin it on a linear axis (which one could argue against, but that's
355
367
// a separate issue)
356
368
if ( dummyAx . dtick . charAt ( 0 ) === 'M' ) {
357
- binStart = autoShiftMonthBins ( binStart , data , dummyAx . dtick , dataMin , calendar ) ;
369
+ binStart = autoShiftMonthBins ( binStart , data , finalSize , dataMin , calendar ) ;
358
370
}
359
371
360
372
// calculate the endpoint for nonlinear ticks - you have to
361
373
// just increment until you're done
362
374
binEnd = binStart ;
363
375
bincount = 0 ;
364
376
while ( binEnd <= dataMax ) {
365
- binEnd = axes . tickIncrement ( binEnd , dummyAx . dtick , false , calendar ) ;
377
+ binEnd = axes . tickIncrement ( binEnd , finalSize , false , calendar ) ;
366
378
bincount ++ ;
367
379
}
368
380
}
369
381
370
382
return {
371
383
start : ax . c2r ( binStart , 0 , calendar ) ,
372
384
end : ax . c2r ( binEnd , 0 , calendar ) ,
373
- size : dummyAx . dtick ,
385
+ size : finalSize ,
374
386
_dataSpan : dataMax - dataMin
375
387
} ;
376
388
} ;
0 commit comments