Skip to content

Commit

Permalink
Improve label capacity calculation
Browse files Browse the repository at this point in the history
  • Loading branch information
nagix committed May 24, 2019
1 parent bd3ab17 commit 998e43b
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 43 deletions.
86 changes: 56 additions & 30 deletions src/scales/scale.time.js
Original file line number Diff line number Diff line change
Expand Up @@ -272,23 +272,51 @@ function determineStepSize(min, max, unit, capacity) {
return factor;
}

function determineMajorUnit(unit) {
for (var i = UNITS.indexOf(unit) + 1, ilen = UNITS.length; i < ilen; ++i) {
if (INTERVALS[UNITS[i]].common) {
return UNITS[i];
}
}
}

/**
* Figures out what unit results in an appropriate number of auto-generated ticks
*/
function determineUnitForAutoTicks(minUnit, min, max, capacity) {
function determineUnitsForAutoTicks(scale) {
var timeOpts = scale.options.time;
var minor = timeOpts.unit;
var max = scale.max;
var range = max - scale.min;
var ilen = UNITS.length;
var i, interval, factor;

for (i = UNITS.indexOf(minUnit); i < ilen - 1; ++i) {
interval = INTERVALS[UNITS[i]];
factor = interval.steps ? interval.steps[interval.steps.length - 1] : MAX_INTEGER;

if (interval.common && Math.ceil((max - min) / (factor * interval.size)) <= capacity) {
return UNITS[i];
var i, major, interval, steps, factor, capacity;

if (minor) {
major = determineMajorUnit(minor);
capacity = scale.getLabelCapacity(max, minor, major);
} else {
for (i = UNITS.indexOf(timeOpts.minUnit); i < ilen; ++i) {
minor = UNITS[i];
major = determineMajorUnit(minor);
interval = INTERVALS[minor];

if (interval.common) {
steps = interval.steps;
factor = steps ? steps[steps.length - 1] : MAX_INTEGER;
capacity = scale.getLabelCapacity(max, minor, major);

if (Math.ceil(range / (factor * interval.size)) <= capacity) {
break;
}
}
}
}

return UNITS[ilen - 1];
return {
minor: minor,
major: major,
capacity: capacity
};
}

/**
Expand All @@ -308,26 +336,21 @@ function determineUnitForFormatting(scale, ticks, minUnit, min, max) {
return UNITS[minUnit ? UNITS.indexOf(minUnit) : 0];
}

function determineMajorUnit(unit) {
for (var i = UNITS.indexOf(unit) + 1, ilen = UNITS.length; i < ilen; ++i) {
if (INTERVALS[UNITS[i]].common) {
return UNITS[i];
}
}
}

/**
* Generates a maximum of `capacity` timestamps between min and max, rounded to the
* `minor` unit, aligned on the `major` unit and using the given scale time `options`.
* Important: this method can return ticks outside the min and max range, it's the
* responsibility of the calling code to clamp values if needed.
*/
function generate(scale, min, max, capacity) {
function generate(scale) {
var min = scale.min;
var max = scale.max;
var adapter = scale._adapter;
var options = scale.options;
var timeOpts = options.time;
var minor = timeOpts.unit || determineUnitForAutoTicks(timeOpts.minUnit, min, max, capacity);
var major = determineMajorUnit(minor);
var units = determineUnitsForAutoTicks(scale);
var minor = units.minor;
var major = units.major;
var stepSize = valueOrDefault(timeOpts.stepSize, timeOpts.unitStepSize);
var weekday = minor === 'week' ? timeOpts.isoWeekday : false;
var majorTicksEnabled = options.ticks.major.enabled;
Expand All @@ -338,7 +361,7 @@ function generate(scale, min, max, capacity) {
var time;

if (!stepSize) {
stepSize = determineStepSize(min, max, minor, capacity);
stepSize = determineStepSize(min, max, minor, units.capacity);
}

// For 'week' unit, handle the first day of week option
Expand Down Expand Up @@ -604,7 +627,7 @@ module.exports = Scale.extend({
break;
case 'auto':
default:
timestamps = generate(me, min, max, me.getLabelCapacity(min), options);
timestamps = generate(me);
}

if (options.bounds === 'ticks' && timestamps.length) {
Expand Down Expand Up @@ -777,15 +800,14 @@ module.exports = Scale.extend({
/**
* @private
*/
getLabelCapacity: function(exampleTime) {
getLabelCapacity: function(exampleTime, minor, major) {
var me = this;
var timeOpts = me.options.time;
var options = me.options;
var timeOpts = options.time;
var displayFormats = timeOpts.displayFormats;
var margins = me.margins;

// pick the longest format (milliseconds) for guestimation
var format = displayFormats[timeOpts.unit] || displayFormats.millisecond;
var exampleLabel = me.tickFormatFunction(exampleTime, 0, ticksFromTimestamps(me, [exampleTime], me._majorUnit), format);
var format = displayFormats[minor || timeOpts.unit] || displayFormats.millisecond;
var exampleLabel = me.tickFormatFunction(exampleTime, 0, [], format);
var size = me._getLabelSize(exampleLabel);

// Using margins instead of padding because padding is not calculated
Expand All @@ -795,10 +817,14 @@ module.exports = Scale.extend({
? (me.width - margins.left - margins.right) / size.w
: (me.height - margins.top - margins.bottom) / size.h);

if (me.options.offset) {
if (options.offset) {
capacity--;
}

if (major && options.ticks.major.enabled) {
capacity = Math.min(capacity, me.getLabelCapacity(exampleTime, major));
}

return capacity > 0 ? capacity : 1;
}
});
Expand Down
44 changes: 31 additions & 13 deletions test/specs/scale.time.tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ describe('Time scale tests', function() {

var scaleOptions = Chart.scaleService.getScaleDefaults('time');
var scale = createScale(mockData, scaleOptions);
scale.update(1000, 200);
scale.update(500, 200);
var ticks = getTicksLabels(scale);

// `bounds === 'data'`: first and last ticks removed since outside the data range
Expand All @@ -135,7 +135,7 @@ describe('Time scale tests', function() {
labels: [newDateFromRef(0), newDateFromRef(1), newDateFromRef(2), newDateFromRef(4), newDateFromRef(6), newDateFromRef(7), newDateFromRef(9)], // days
};
var scale = createScale(mockData, Chart.scaleService.getScaleDefaults('time'));
scale.update(1000, 200);
scale.update(500, 200);
var ticks = getTicksLabels(scale);

// `bounds === 'data'`: first and last ticks removed since outside the data range
Expand Down Expand Up @@ -184,7 +184,7 @@ describe('Time scale tests', function() {
});

var xScale = chart.scales.xScale0;
xScale.update(800, 200);
xScale.update(400, 200);
var ticks = getTicksLabels(xScale);

// `bounds === 'data'`: first and last ticks removed since outside the data range
Expand Down Expand Up @@ -233,7 +233,7 @@ describe('Time scale tests', function() {
});

var tScale = chart.scales.tScale0;
tScale.update(800, 200);
tScale.update(400, 200);
var ticks = getTicksLabels(tScale);

// `bounds === 'data'`: first and last ticks removed since outside the data range
Expand Down Expand Up @@ -603,7 +603,7 @@ describe('Time scale tests', function() {
});

this.scale = this.chart.scales.xScale0;
this.scale.update(800, 200);
this.scale.update(300, 200);
});

it('should be bounded by nearest step\'s year start and end', function() {
Expand Down Expand Up @@ -703,8 +703,11 @@ describe('Time scale tests', function() {

expect(scale._ticks.map(function(tick) {
return tick.major;
})).toEqual([true, false, false, false, true]);
expect(scale.ticks).toEqual(['<8:00:00 pm>', '<8:00:15 pm>', '<8:00:30 pm>', '<8:00:45 pm>', '<8:01:00 pm>']);
})).toEqual([true, false, false, false, false, false, true]);
expect(scale.ticks).toEqual([
'<8:00:00 pm>', '<8:00:10 pm>', '<8:00:20 pm>', '<8:00:30 pm>',
'<8:00:40 pm>', '<8:00:50 pm>', '<8:01:00 pm>'
]);
});

it('should update ticks.callback correctly', function() {
Expand All @@ -715,7 +718,10 @@ describe('Time scale tests', function() {
return '{' + value + '}';
};
chart.update();
expect(scale.ticks).toEqual(['{8:00:00 pm}', '{8:00:15 pm}', '{8:00:30 pm}', '{8:00:45 pm}', '{8:01:00 pm}']);
expect(scale.ticks).toEqual([
'{8:00:00 pm}', '{8:00:10 pm}', '{8:00:20 pm}', '{8:00:30 pm}',
'{8:00:40 pm}', '{8:00:50 pm}', '{8:01:00 pm}'
]);
});
});

Expand Down Expand Up @@ -763,8 +769,11 @@ describe('Time scale tests', function() {

expect(scale._ticks.map(function(tick) {
return tick.major;
})).toEqual([true, false, false, false, true]);
expect(scale.ticks).toEqual(['[[8:00 pm]]', '(8:00:15 pm)', '(8:00:30 pm)', '(8:00:45 pm)', '[[8:01 pm]]']);
})).toEqual([true, false, false, false, false, false, true]);
expect(scale.ticks).toEqual([
'[[8:00 pm]]', '(8:00:10 pm)', '(8:00:20 pm)', '(8:00:30 pm)',
'(8:00:40 pm)', '(8:00:50 pm)', '[[8:01 pm]]'
]);
});

it('should only use ticks.minor callback if ticks.major.enabled is false', function() {
Expand All @@ -773,7 +782,10 @@ describe('Time scale tests', function() {

chart.options.scales.xAxes[0].ticks.major.enabled = false;
chart.update();
expect(scale.ticks).toEqual(['(8:00:00 pm)', '(8:00:15 pm)', '(8:00:30 pm)', '(8:00:45 pm)', '(8:01:00 pm)']);
expect(scale.ticks).toEqual([
'(8:00:00 pm)', '(8:00:10 pm)', '(8:00:20 pm)', '(8:00:30 pm)',
'(8:00:40 pm)', '(8:00:50 pm)', '(8:01:00 pm)'
]);
});

it('should use ticks.callback if ticks.major.callback is omitted', function() {
Expand All @@ -782,7 +794,10 @@ describe('Time scale tests', function() {

chart.options.scales.xAxes[0].ticks.major.callback = undefined;
chart.update();
expect(scale.ticks).toEqual(['<8:00 pm>', '(8:00:15 pm)', '(8:00:30 pm)', '(8:00:45 pm)', '<8:01 pm>']);
expect(scale.ticks).toEqual([
'<8:00 pm>', '(8:00:10 pm)', '(8:00:20 pm)', '(8:00:30 pm)',
'(8:00:40 pm)', '(8:00:50 pm)', '<8:01 pm>'
]);
});

it('should use ticks.callback if ticks.minor.callback is omitted', function() {
Expand All @@ -791,7 +806,10 @@ describe('Time scale tests', function() {

chart.options.scales.xAxes[0].ticks.minor.callback = undefined;
chart.update();
expect(scale.ticks).toEqual(['[[8:00 pm]]', '<8:00:15 pm>', '<8:00:30 pm>', '<8:00:45 pm>', '[[8:01 pm]]']);
expect(scale.ticks).toEqual([
'[[8:00 pm]]', '<8:00:10 pm>', '<8:00:20 pm>', '<8:00:30 pm>',
'<8:00:40 pm>', '<8:00:50 pm>', '[[8:01 pm]]'
]);
});
});

Expand Down

0 comments on commit 998e43b

Please sign in to comment.