Skip to content

Commit a8e4802

Browse files
committed
extend Axes.autoBin to find start/end with fixed size
1 parent ab20fae commit a8e4802

File tree

1 file changed

+47
-35
lines changed

1 file changed

+47
-35
lines changed

src/plots/cartesian/axes.js

Lines changed: 47 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ var Color = require('../../components/color');
2121
var Drawing = require('../../components/drawing');
2222

2323
var axAttrs = require('./layout_attributes');
24+
var cleanTicks = require('./clean_ticks');
2425

2526
var constants = require('../../constants/numerical');
2627
var 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

Comments
 (0)