Skip to content

Commit

Permalink
Add sampling to limit time computing label sizes
Browse files Browse the repository at this point in the history
  • Loading branch information
benmccann committed Jun 28, 2019
1 parent 6e3e4a1 commit d437234
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 32 deletions.
3 changes: 1 addition & 2 deletions src/controllers/controller.bar.js
Expand Up @@ -33,14 +33,13 @@ defaults._set('bar', {
*/
function computeMinSampleSize(scale, pixels) {
var min = scale.isHorizontal() ? scale.width : scale.height;
var ticks = scale.getTicks();
var prev, curr, i, ilen;

for (i = 1, ilen = pixels.length; i < ilen; ++i) {
min = Math.min(min, Math.abs(pixels[i] - pixels[i - 1]));
}

for (i = 0, ilen = ticks.length; i < ilen; ++i) {
for (i = 0, ilen = scale.getTicks().length; i < ilen; ++i) {
curr = scale.getPixelForTick(i);
min = i > 0 ? Math.min(min, curr - prev) : min;
prev = curr;
Expand Down
98 changes: 69 additions & 29 deletions src/core/core.scale.js
Expand Up @@ -65,11 +65,30 @@ defaults._set('scale', {
}
});

function sample(arr, size) {
var shuffled = arr.slice(0);
var i = arr.length;
var min = i - size;
var tmp, index;

if (size >= min) {
return arr;
}

while (i-- > min) {
index = Math.floor((i + 1) * Math.random());
tmp = shuffled[index];
shuffled[index] = shuffled[i];
shuffled[i] = tmp;
}
return shuffled.slice(min);
}

function getPixelForGridLine(scale, index, offsetGridLines) {
var lineValue = scale.getPixelForTick(index);

if (offsetGridLines) {
if (scale.getTicks().length === 1) {
if (scale._ticks.length === 1) {
lineValue -= scale.isHorizontal() ?
Math.max(lineValue - scale.left, scale.right - lineValue) :
Math.max(lineValue - scale.top, scale.bottom - lineValue);
Expand Down Expand Up @@ -304,43 +323,39 @@ var Scale = Element.extend({
ticks = [];
for (i = 0, ilen = me.ticks.length; i < ilen; ++i) {
ticks.push({
index: i,
_index: i,
value: me.ticks[i],
major: false
});
}
}
me._numTicks = ticks.length;

me.beforeTickToLabelConversion();

// New implementations should return the formatted tick labels but for BACKWARD
// COMPAT, we still support no return (`this.ticks` internally changed by calling
// this method and supposed to contain only string values).
labels = me.convertTicksToLabels(ticks) || me.ticks;

me.afterTickToLabelConversion();

// IMPORTANT: below this point, we consider that `this.ticks` will NEVER change!
me.ticks = labels; // BACKWARD COMPATIBILITY

// BACKWARD COMPAT: synchronize `_ticks` with labels (so potentially `this.ticks`)
for (i = 0, ilen = labels.length; i < ilen; ++i) {
ticks[i].label = labels[i];
}
// Compute tick rotation and fit using a sampled sub-set of labels
// We generally don't need to compute the size of every single label for determining scale size
me._ticks = sample(ticks, tickOpts.sampleSize || ticks.length);

me._ticks = ticks;
labels = me._convertTicksToLabels(me._ticks);

// Tick Rotation
me.beforeCalculateTickRotation();
me.calculateTickRotation();
me.afterCalculateTickRotation();
// Fit

me.beforeFit();
me.fit();
me.afterFit();

// Auto-skip
me._ticks = tickOpts.display && tickOpts.autoSkip ? me._autoSkip(me.getTicks()) : me.getTicks();
me._ticks = ticks = tickOpts.display && tickOpts.autoSkip ? me._autoSkip(ticks) : ticks;

if (tickOpts.sampleSize) {
// Generate labels using all non-skipped ticks
labels = me._convertTicksToLabels(ticks);
}

me.ticks = labels; // BACKWARD COMPATIBILITY

// IMPORTANT: after this point, we consider that `this.ticks` will NEVER change!

me.afterUpdate();

Expand Down Expand Up @@ -524,7 +539,7 @@ var Scale = Element.extend({
minSize.height = Math.min(me.maxHeight, minSize.height + labelHeight + tickPadding);

var offsetLeft = me.getPixelForTick(0) - me.left;
var offsetRight = me.right - me.getPixelForTick(me._numTicks - 1);
var offsetRight = me.right - me.getPixelForTick(me._ticks.length - 1);
var paddingLeft, paddingRight;

// Ensure that our ticks are always inside the canvas. When rotated, ticks are right aligned
Expand Down Expand Up @@ -621,6 +636,31 @@ var Scale = Element.extend({
return rawValue;
},

_convertTicksToLabels: function(ticks) {
var me = this;
var labels, i, ilen;

me.ticks = ticks.map(function(tick) {
return tick.value;
});

me.beforeTickToLabelConversion();

// New implementations should return the formatted tick labels but for BACKWARD
// COMPAT, we still support no return (`this.ticks` internally changed by calling
// this method and supposed to contain only string values).
labels = me.convertTicksToLabels(ticks) || me.ticks;

me.afterTickToLabelConversion();

// BACKWARD COMPAT: synchronize `_ticks` with labels (so potentially `this.ticks`)
for (i = 0, ilen = labels.length; i < ilen; ++i) {
ticks[i].label = labels[i];
}

return labels;
},

/**
* @private
*/
Expand Down Expand Up @@ -705,7 +745,7 @@ var Scale = Element.extend({
getPixelForTick: function(index) {
var me = this;
var offset = me.options.offset;
var numTicks = me._numTicks;
var numTicks = me._ticks.length;
if (index < 0 || index > numTicks - 1) {
return null;
}
Expand Down Expand Up @@ -894,8 +934,8 @@ var Scale = Element.extend({
tickEnd = me.left + tl;
}

helpers.each(ticks, function(tick) {
var index = tick.index;
helpers.each(ticks, function(tick, i) {
var index = tick._index;
var label = tick.label;
var tickFont = tick.major ? tickFonts.major : tickFonts.minor;
var lineHeight = tickFont.lineHeight;
Expand All @@ -916,7 +956,7 @@ var Scale = Element.extend({
// Common properties
var tx1, ty1, tx2, ty2, x1, y1, x2, y2, labelX, labelY, textOffset, textAlign;
var labelCount = helpers.isArray(label) ? label.length : 1;
var lineValue = getPixelForGridLine(me, index, gridLines.offsetGridLines);
var lineValue = getPixelForGridLine(me, i, gridLines.offsetGridLines);

if (isHorizontal) {
var labelYOffset = tl + tickPadding;
Expand All @@ -928,7 +968,7 @@ var Scale = Element.extend({
tx1 = tx2 = x1 = x2 = alignPixel(chart, lineValue, lineWidth);
ty1 = tickStart;
ty2 = tickEnd;
labelX = me.getPixelForTick(index) + labelOffset; // x values for optionTicks (need to consider offsetLabel option)
labelX = me.getPixelForTick(i) + labelOffset; // x values for optionTicks (need to consider offsetLabel option)

if (position === 'top') {
y1 = alignPixel(chart, chartArea.top, axisWidth) + axisWidth / 2;
Expand All @@ -953,7 +993,7 @@ var Scale = Element.extend({
tx1 = tickStart;
tx2 = tickEnd;
ty1 = ty2 = y1 = y2 = alignPixel(chart, lineValue, lineWidth);
labelY = me.getPixelForTick(index) + labelOffset;
labelY = me.getPixelForTick(i) + labelOffset;
textOffset = (1 - labelCount) * lineHeight / 2;

if (position === 'left') {
Expand Down
2 changes: 1 addition & 1 deletion src/scales/scale.time.js
Expand Up @@ -430,7 +430,7 @@ function ticksFromTimestamps(scale, values, majorUnit) {
map[value] = i;

ticks.push({
index: i,
_index: i,
value: value,
major: false
});
Expand Down

0 comments on commit d437234

Please sign in to comment.