@@ -21,6 +21,7 @@ var Color = require('../../components/color');
2121var Drawing = require ( '../../components/drawing' ) ;
2222
2323var axAttrs = require ( './layout_attributes' ) ;
24+ var cleanTicks = require ( './clean_ticks' ) ;
2425
2526var constants = require ( '../../constants/numerical' ) ;
2627var ONEAVGYEAR = constants . ONEAVGYEAR ;
@@ -280,43 +281,22 @@ axes.saveShowSpikeInitial = function(gd, overwrite) {
280281 return hasOneAxisChanged ;
281282} ;
282283
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 ) ;
288287
289288 if ( ax . type === 'category' ) {
290289 return {
291290 start : dataMin - 0.5 ,
292291 end : dataMax + 0.5 ,
293- size : 1 ,
292+ size : Math . max ( 1 , Math . round ( size ) || 1 ) ,
294293 _dataSpan : dataMax - dataMin ,
295294 } ;
296295 }
297296
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 ;
318298
319- // piggyback off autotick code to make "nice" bin sizes
299+ // piggyback off tick code to make "nice" bin sizes and edges
320300 var dummyAx ;
321301 if ( ax . type === 'log' ) {
322302 dummyAx = {
@@ -333,19 +313,51 @@ axes.autoBin = function(data, ax, nbins, is2d, calendar) {
333313 }
334314 axes . setConvert ( dummyAx ) ;
335315
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 ;
337349 var binStart = axes . tickIncrement (
338- axes . tickFirst ( dummyAx ) , dummyAx . dtick , 'reverse' , calendar ) ;
350+ axes . tickFirst ( dummyAx ) , finalSize , 'reverse' , calendar ) ;
339351 var binEnd , bincount ;
340352
341353 // check for too many data points right at the edges of bins
342354 // (>50% within 1% of bin edges) or all data points integral
343355 // and offset the bins accordingly
344- if ( typeof dummyAx . dtick === 'number' ) {
356+ if ( typeof finalSize === 'number' ) {
345357 binStart = autoShiftNumericBins ( binStart , data , dummyAx , dataMin , dataMax ) ;
346358
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 ;
349361 }
350362 else {
351363 // 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) {
354366 // we bin it on a linear axis (which one could argue against, but that's
355367 // a separate issue)
356368 if ( dummyAx . dtick . charAt ( 0 ) === 'M' ) {
357- binStart = autoShiftMonthBins ( binStart , data , dummyAx . dtick , dataMin , calendar ) ;
369+ binStart = autoShiftMonthBins ( binStart , data , finalSize , dataMin , calendar ) ;
358370 }
359371
360372 // calculate the endpoint for nonlinear ticks - you have to
361373 // just increment until you're done
362374 binEnd = binStart ;
363375 bincount = 0 ;
364376 while ( binEnd <= dataMax ) {
365- binEnd = axes . tickIncrement ( binEnd , dummyAx . dtick , false , calendar ) ;
377+ binEnd = axes . tickIncrement ( binEnd , finalSize , false , calendar ) ;
366378 bincount ++ ;
367379 }
368380 }
369381
370382 return {
371383 start : ax . c2r ( binStart , 0 , calendar ) ,
372384 end : ax . c2r ( binEnd , 0 , calendar ) ,
373- size : dummyAx . dtick ,
385+ size : finalSize ,
374386 _dataSpan : dataMax - dataMin
375387 } ;
376388} ;
0 commit comments