Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change ticks.mode to scale.distribution #4582

Merged
merged 1 commit into from
Jul 30, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 77 additions & 51 deletions src/scales/scale.time.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,22 @@ function sorter(a, b) {
return a - b;
}

function arrayUnique(items) {
var hash = {};
var out = [];
var i, ilen, item;

for (i = 0, ilen = items.length; i < ilen; ++i) {
item = items[i];
if (!hash[item]) {
hash[item] = true;
out.push(item);
}
}

return out;
}

/**
* Returns an array of {time, pos} objects used to interpolate a specific `time` or position
* (`pos`) on the scale, by searching entries before and after the requested value. `pos` is
Expand All @@ -73,31 +89,33 @@ function sorter(a, b) {
* to create the lookup table. The table ALWAYS contains at least two items: min and max.
*
* @param {Number[]} timestamps - timestamps sorted from lowest to highest.
* @param {Boolean} linear - If true, timestamps will be spread linearly along the min/max
* range, so basically, the table will contains only two items: {min, 0} and {max, 1}. If
* false, timestamps will be positioned at the same distance from each other. In this case,
* only timestamps that break the time linearity are registered, meaning that in the best
* case, all timestamps are linear, the table contains only min and max.
* @param {String} distribution - If 'linear', timestamps will be spread linearly along the min
* and max range, so basically, the table will contains only two items: {min, 0} and {max, 1}.
* If 'series', timestamps will be positioned at the same distance from each other. In this
* case, only timestamps that break the time linearity are registered, meaning that in the
* best case, all timestamps are linear, the table contains only min and max.
*/
function buildLookupTable(timestamps, min, max, linear) {
if (linear || !timestamps.length) {
function buildLookupTable(timestamps, min, max, distribution) {
if (distribution === 'linear' || !timestamps.length) {
return [
{time: min, pos: 0},
{time: max, pos: 1}
];
}

var table = [];
var items = timestamps.slice(0);
var items = [min];
var i, ilen, prev, curr, next;

if (min < timestamps[0]) {
items.unshift(min);
}
if (max > timestamps[timestamps.length - 1]) {
items.push(max);
for (i = 0, ilen = timestamps.length; i < ilen; ++i) {
curr = timestamps[i];
if (curr > min && curr < max) {
items.push(curr);
}
}

items.push(max);

for (i = 0, ilen = items.length; i < ilen; ++i) {
next = items[i + 1];
prev = items[i - 1];
Expand Down Expand Up @@ -334,6 +352,15 @@ module.exports = function(Chart) {
var defaultConfig = {
position: 'bottom',

/**
* Data distribution along the scale:
* - 'linear': data are spread according to their time (distances can vary),
* - 'series': data are spread at the same distance from each other.
* @see https://github.com/chartjs/Chart.js/pull/4507
* @since 2.7.0
*/
distribution: 'linear',

time: {
parser: false, // false == a pattern string from http://momentjs.com/docs/#/parsing/string-format/ or a custom callback that converts its argument to a moment
format: false, // DEPRECATED false == date objects, moment object, callback or a pattern string from http://momentjs.com/docs/#/parsing/string-format/
Expand All @@ -359,15 +386,6 @@ module.exports = function(Chart) {
ticks: {
autoSkip: false,

/**
* Ticks distribution along the scale:
* - 'linear': ticks and data are spread according to their time (distances can vary),
* - 'series': ticks and data are spread at the same distance from each other.
* @see https://github.com/chartjs/Chart.js/pull/4507
* @since 2.7.0
*/
mode: 'linear',

/**
* Ticks generation input values:
* - 'auto': generates "optimal" ticks based on scale size and time options.
Expand Down Expand Up @@ -430,44 +448,54 @@ module.exports = function(Chart) {
var me = this;
var chart = me.chart;
var options = me.options;
var datasets = chart.data.datasets || [];
var min = parse(options.time.min, me) || MAX_INTEGER;
var max = parse(options.time.max, me) || MIN_INTEGER;
var timestamps = [];
var datasets = [];
var labels = [];
var i, j, ilen, jlen, data, timestamp;

// Convert labels to timestamps
for (i = 0, ilen = chart.data.labels.length; i < ilen; ++i) {
timestamp = parse(chart.data.labels[i], me);
min = Math.min(min, timestamp);
max = Math.max(max, timestamp);
labels.push(timestamp);
labels.push(parse(chart.data.labels[i], me));
}

// Convert data to timestamps
for (i = 0, ilen = datasets.length; i < ilen; ++i) {
for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) {
if (chart.isDatasetVisible(i)) {
data = datasets[i].data;
data = chart.data.datasets[i].data;

// Let's consider that all data have the same format.
if (helpers.isObject(data[0])) {
timestamps[i] = [];
datasets[i] = [];

for (j = 0, jlen = data.length; j < jlen; ++j) {
timestamp = parse(data[j], me);
min = Math.min(min, timestamp);
max = Math.max(max, timestamp);
timestamps[i][j] = timestamp;
timestamps.push(timestamp);
datasets[i][j] = timestamp;
}
} else {
timestamps[i] = labels.slice(0);
timestamps.push.apply(timestamps, labels);
datasets[i] = labels.slice(0);
}
} else {
timestamps[i] = [];
datasets[i] = [];
}
}

if (labels.length) {
// Sort labels **after** data have been converted
labels = arrayUnique(labels).sort(sorter);
min = Math.min(min, labels[0]);
max = Math.max(max, labels[labels.length - 1]);
}

if (timestamps.length) {
timestamps = arrayUnique(timestamps).sort(sorter);
min = Math.min(min, timestamps[0]);
max = Math.max(max, timestamps[timestamps.length - 1]);
}

// In case there is no valid min/max, let's use today limits
min = min === MAX_INTEGER ? +moment().startOf('day') : min;
max = max === MIN_INTEGER ? +moment().endOf('day') + 1 : max;
Expand All @@ -477,36 +505,36 @@ module.exports = function(Chart) {
me.max = Math.max(min + 1, max);

// PRIVATE
me._datasets = timestamps;
me._horizontal = me.isHorizontal();
me._labels = labels.sort(sorter); // Sort labels **after** data have been converted
me._table = [];
me._timestamps = {
data: timestamps,
datasets: datasets,
labels: labels
};
},

buildTicks: function() {
var me = this;
var min = me.min;
var max = me.max;
var timeOpts = me.options.time;
var ticksOpts = me.options.ticks;
var options = me.options;
var timeOpts = options.time;
var ticksOpts = options.ticks;
var formats = timeOpts.displayFormats;
var capacity = me.getLabelCapacity(min);
var unit = timeOpts.unit || determineUnit(timeOpts.minUnit, min, max, capacity);
var majorUnit = determineMajorUnit(unit);
var timestamps = [];
var ticks = [];
var hash = {};
var i, ilen, timestamp;

switch (ticksOpts.source) {
case 'data':
for (i = 0, ilen = me._datasets.length; i < ilen; ++i) {
timestamps.push.apply(timestamps, me._datasets[i]);
}
timestamps.sort(sorter);
timestamps = me._timestamps.data;
break;
case 'labels':
timestamps = me._labels;
timestamps = me._timestamps.labels;
break;
case 'auto':
default:
Expand All @@ -522,12 +550,10 @@ module.exports = function(Chart) {
min = parse(timeOpts.min, me) || min;
max = parse(timeOpts.max, me) || max;

// Remove ticks outside the min/max range and duplicated entries
// Remove ticks outside the min/max range
for (i = 0, ilen = timestamps.length; i < ilen; ++i) {
timestamp = timestamps[i];
if (timestamp >= min && timestamp <= max && !hash[timestamp]) {
// hash is used to efficiently detect timestamp duplicates
hash[timestamp] = true;
if (timestamp >= min && timestamp <= max) {
ticks.push(timestamp);
}
}
Expand All @@ -540,7 +566,7 @@ module.exports = function(Chart) {
me._majorUnit = majorUnit;
me._minorFormat = formats[unit];
me._majorFormat = formats[majorUnit];
me._table = buildLookupTable(ticks, min, max, ticksOpts.mode === 'linear');
me._table = buildLookupTable(me._timestamps.data, min, max, options.distribution);

return ticksFromTimestamps(ticks, majorUnit);
},
Expand Down Expand Up @@ -609,7 +635,7 @@ module.exports = function(Chart) {
var time = null;

if (index !== undefined && datasetIndex !== undefined) {
time = me._datasets[datasetIndex][index];
time = me._timestamps.datasets[datasetIndex][index];
}

if (time === null) {
Expand Down
Loading