mirror of
https://github.com/chartjs/Chart.js.git
synced 2024-10-06 12:19:08 +02:00
Move autoSkip related functions to separate file (#8691)
This commit is contained in:
parent
1460477989
commit
bd9bc69c0c
169
src/core/core.scale.autoskip.js
Normal file
169
src/core/core.scale.autoskip.js
Normal file
@ -0,0 +1,169 @@
|
||||
import {isNullOrUndef, valueOrDefault} from '../helpers/helpers.core';
|
||||
import {_factorize} from '../helpers/helpers.math';
|
||||
|
||||
|
||||
/**
|
||||
* @typedef { import("./core.controller").default } Chart
|
||||
* @typedef {{value:number | string, label?:string, major?:boolean, $context?:any}} Tick
|
||||
*/
|
||||
|
||||
/**
|
||||
* Returns a subset of ticks to be plotted to avoid overlapping labels.
|
||||
* @param {import('./core.scale').default} scale
|
||||
* @param {Tick[]} ticks
|
||||
* @return {Tick[]}
|
||||
* @private
|
||||
*/
|
||||
export function autoSkip(scale, ticks) {
|
||||
const tickOpts = scale.options.ticks;
|
||||
const ticksLimit = tickOpts.maxTicksLimit || determineMaxTicks(scale);
|
||||
const majorIndices = tickOpts.major.enabled ? getMajorIndices(ticks) : [];
|
||||
const numMajorIndices = majorIndices.length;
|
||||
const first = majorIndices[0];
|
||||
const last = majorIndices[numMajorIndices - 1];
|
||||
const newTicks = [];
|
||||
|
||||
// If there are too many major ticks to display them all
|
||||
if (numMajorIndices > ticksLimit) {
|
||||
skipMajors(ticks, newTicks, majorIndices, numMajorIndices / ticksLimit);
|
||||
return newTicks;
|
||||
}
|
||||
|
||||
const spacing = calculateSpacing(majorIndices, ticks, ticksLimit);
|
||||
|
||||
if (numMajorIndices > 0) {
|
||||
let i, ilen;
|
||||
const avgMajorSpacing = numMajorIndices > 1 ? Math.round((last - first) / (numMajorIndices - 1)) : null;
|
||||
skip(ticks, newTicks, spacing, isNullOrUndef(avgMajorSpacing) ? 0 : first - avgMajorSpacing, first);
|
||||
for (i = 0, ilen = numMajorIndices - 1; i < ilen; i++) {
|
||||
skip(ticks, newTicks, spacing, majorIndices[i], majorIndices[i + 1]);
|
||||
}
|
||||
skip(ticks, newTicks, spacing, last, isNullOrUndef(avgMajorSpacing) ? ticks.length : last + avgMajorSpacing);
|
||||
return newTicks;
|
||||
}
|
||||
skip(ticks, newTicks, spacing);
|
||||
return newTicks;
|
||||
}
|
||||
|
||||
function determineMaxTicks(scale) {
|
||||
const offset = scale.options.offset;
|
||||
const tickLength = scale._tickSize();
|
||||
const maxScale = scale._length / tickLength + (offset ? 0 : 1);
|
||||
const maxChart = scale._maxLength / tickLength;
|
||||
return Math.floor(Math.min(maxScale, maxChart));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number[]} majorIndices
|
||||
* @param {Tick[]} ticks
|
||||
* @param {number} ticksLimit
|
||||
*/
|
||||
function calculateSpacing(majorIndices, ticks, ticksLimit) {
|
||||
const evenMajorSpacing = getEvenSpacing(majorIndices);
|
||||
const spacing = ticks.length / ticksLimit;
|
||||
|
||||
// If the major ticks are evenly spaced apart, place the minor ticks
|
||||
// so that they divide the major ticks into even chunks
|
||||
if (!evenMajorSpacing) {
|
||||
return Math.max(spacing, 1);
|
||||
}
|
||||
|
||||
const factors = _factorize(evenMajorSpacing);
|
||||
for (let i = 0, ilen = factors.length - 1; i < ilen; i++) {
|
||||
const factor = factors[i];
|
||||
if (factor > spacing) {
|
||||
return factor;
|
||||
}
|
||||
}
|
||||
return Math.max(spacing, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Tick[]} ticks
|
||||
*/
|
||||
function getMajorIndices(ticks) {
|
||||
const result = [];
|
||||
let i, ilen;
|
||||
for (i = 0, ilen = ticks.length; i < ilen; i++) {
|
||||
if (ticks[i].major) {
|
||||
result.push(i);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Tick[]} ticks
|
||||
* @param {Tick[]} newTicks
|
||||
* @param {number[]} majorIndices
|
||||
* @param {number} spacing
|
||||
*/
|
||||
function skipMajors(ticks, newTicks, majorIndices, spacing) {
|
||||
let count = 0;
|
||||
let next = majorIndices[0];
|
||||
let i;
|
||||
|
||||
spacing = Math.ceil(spacing);
|
||||
for (i = 0; i < ticks.length; i++) {
|
||||
if (i === next) {
|
||||
newTicks.push(ticks[i]);
|
||||
count++;
|
||||
next = majorIndices[count * spacing];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Tick[]} ticks
|
||||
* @param {Tick[]} newTicks
|
||||
* @param {number} spacing
|
||||
* @param {number} [majorStart]
|
||||
* @param {number} [majorEnd]
|
||||
*/
|
||||
function skip(ticks, newTicks, spacing, majorStart, majorEnd) {
|
||||
const start = valueOrDefault(majorStart, 0);
|
||||
const end = Math.min(valueOrDefault(majorEnd, ticks.length), ticks.length);
|
||||
let count = 0;
|
||||
let length, i, next;
|
||||
|
||||
spacing = Math.ceil(spacing);
|
||||
if (majorEnd) {
|
||||
length = majorEnd - majorStart;
|
||||
spacing = length / Math.floor(length / spacing);
|
||||
}
|
||||
|
||||
next = start;
|
||||
|
||||
while (next < 0) {
|
||||
count++;
|
||||
next = Math.round(start + count * spacing);
|
||||
}
|
||||
|
||||
for (i = Math.max(start, 0); i < end; i++) {
|
||||
if (i === next) {
|
||||
newTicks.push(ticks[i]);
|
||||
count++;
|
||||
next = Math.round(start + count * spacing);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param {number[]} arr
|
||||
*/
|
||||
function getEvenSpacing(arr) {
|
||||
const len = arr.length;
|
||||
let i, diff;
|
||||
|
||||
if (len < 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (diff = arr[0], i = 1; i < len; ++i) {
|
||||
if (arr[i] - arr[i - 1] !== diff) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return diff;
|
||||
}
|
@ -1,11 +1,12 @@
|
||||
import defaults from './core.defaults';
|
||||
import Element from './core.element';
|
||||
import {_alignPixel, _measureText, renderText, clipArea, unclipArea} from '../helpers/helpers.canvas';
|
||||
import {callback as call, each, finiteOrDefault, isArray, isFinite, isNullOrUndef, isObject, valueOrDefault} from '../helpers/helpers.core';
|
||||
import {_factorize, toDegrees, toRadians, _int16Range, _limitValue, HALF_PI} from '../helpers/helpers.math';
|
||||
import {callback as call, each, finiteOrDefault, isArray, isFinite, isNullOrUndef, isObject} from '../helpers/helpers.core';
|
||||
import {toDegrees, toRadians, _int16Range, _limitValue, HALF_PI} from '../helpers/helpers.math';
|
||||
import {_alignStartEnd, _toLeftRightCenter} from '../helpers/helpers.extras';
|
||||
import {toFont, toPadding} from '../helpers/helpers.options';
|
||||
import Ticks from './core.ticks';
|
||||
import {autoSkip} from './core.scale.autoskip';
|
||||
|
||||
const reverseAlign = (align) => align === 'left' ? 'right' : align === 'right' ? 'left' : align;
|
||||
const offsetFromEdge = (scale, edge, offset) => edge === 'top' || edge === 'left' ? scale[edge] + offset : scale[edge] - offset;
|
||||
@ -192,128 +193,6 @@ function getTitleHeight(options, fallback) {
|
||||
return (lines * font.lineHeight) + padding.height;
|
||||
}
|
||||
|
||||
function determineMaxTicks(scale) {
|
||||
const offset = scale.options.offset;
|
||||
const tickLength = scale._tickSize();
|
||||
const maxScale = scale._length / tickLength + (offset ? 0 : 1);
|
||||
const maxChart = scale._maxLength / tickLength;
|
||||
return Math.floor(Math.min(maxScale, maxChart));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number[]} arr
|
||||
*/
|
||||
function getEvenSpacing(arr) {
|
||||
const len = arr.length;
|
||||
let i, diff;
|
||||
|
||||
if (len < 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (diff = arr[0], i = 1; i < len; ++i) {
|
||||
if (arr[i] - arr[i - 1] !== diff) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return diff;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number[]} majorIndices
|
||||
* @param {Tick[]} ticks
|
||||
* @param {number} ticksLimit
|
||||
*/
|
||||
function calculateSpacing(majorIndices, ticks, ticksLimit) {
|
||||
const evenMajorSpacing = getEvenSpacing(majorIndices);
|
||||
const spacing = ticks.length / ticksLimit;
|
||||
|
||||
// If the major ticks are evenly spaced apart, place the minor ticks
|
||||
// so that they divide the major ticks into even chunks
|
||||
if (!evenMajorSpacing) {
|
||||
return Math.max(spacing, 1);
|
||||
}
|
||||
|
||||
const factors = _factorize(evenMajorSpacing);
|
||||
for (let i = 0, ilen = factors.length - 1; i < ilen; i++) {
|
||||
const factor = factors[i];
|
||||
if (factor > spacing) {
|
||||
return factor;
|
||||
}
|
||||
}
|
||||
return Math.max(spacing, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Tick[]} ticks
|
||||
*/
|
||||
function getMajorIndices(ticks) {
|
||||
const result = [];
|
||||
let i, ilen;
|
||||
for (i = 0, ilen = ticks.length; i < ilen; i++) {
|
||||
if (ticks[i].major) {
|
||||
result.push(i);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Tick[]} ticks
|
||||
* @param {Tick[]} newTicks
|
||||
* @param {number[]} majorIndices
|
||||
* @param {number} spacing
|
||||
*/
|
||||
function skipMajors(ticks, newTicks, majorIndices, spacing) {
|
||||
let count = 0;
|
||||
let next = majorIndices[0];
|
||||
let i;
|
||||
|
||||
spacing = Math.ceil(spacing);
|
||||
for (i = 0; i < ticks.length; i++) {
|
||||
if (i === next) {
|
||||
newTicks.push(ticks[i]);
|
||||
count++;
|
||||
next = majorIndices[count * spacing];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Tick[]} ticks
|
||||
* @param {Tick[]} newTicks
|
||||
* @param {number} spacing
|
||||
* @param {number} [majorStart]
|
||||
* @param {number} [majorEnd]
|
||||
*/
|
||||
function skip(ticks, newTicks, spacing, majorStart, majorEnd) {
|
||||
const start = valueOrDefault(majorStart, 0);
|
||||
const end = Math.min(valueOrDefault(majorEnd, ticks.length), ticks.length);
|
||||
let count = 0;
|
||||
let length, i, next;
|
||||
|
||||
spacing = Math.ceil(spacing);
|
||||
if (majorEnd) {
|
||||
length = majorEnd - majorStart;
|
||||
spacing = length / Math.floor(length / spacing);
|
||||
}
|
||||
|
||||
next = start;
|
||||
|
||||
while (next < 0) {
|
||||
count++;
|
||||
next = Math.round(start + count * spacing);
|
||||
}
|
||||
|
||||
for (i = Math.max(start, 0); i < end; i++) {
|
||||
if (i === next) {
|
||||
newTicks.push(ticks[i]);
|
||||
count++;
|
||||
next = Math.round(start + count * spacing);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function createScaleContext(parent, scale) {
|
||||
return Object.assign(Object.create(parent), {
|
||||
scale,
|
||||
@ -635,7 +514,7 @@ export default class Scale extends Element {
|
||||
|
||||
// Auto-skip
|
||||
if (tickOpts.display && (tickOpts.autoSkip || tickOpts.source === 'auto')) {
|
||||
me.ticks = me._autoSkip(me.ticks);
|
||||
me.ticks = autoSkip(me, me.ticks);
|
||||
me._labelSizes = null;
|
||||
}
|
||||
|
||||
@ -1149,44 +1028,6 @@ export default class Scale extends Element {
|
||||
(me.$context = createScaleContext(me.chart.getContext(), me));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a subset of ticks to be plotted to avoid overlapping labels.
|
||||
* @param {Tick[]} ticks
|
||||
* @return {Tick[]}
|
||||
* @private
|
||||
*/
|
||||
_autoSkip(ticks) {
|
||||
const me = this;
|
||||
const tickOpts = me.options.ticks;
|
||||
const ticksLimit = tickOpts.maxTicksLimit || determineMaxTicks(me);
|
||||
const majorIndices = tickOpts.major.enabled ? getMajorIndices(ticks) : [];
|
||||
const numMajorIndices = majorIndices.length;
|
||||
const first = majorIndices[0];
|
||||
const last = majorIndices[numMajorIndices - 1];
|
||||
const newTicks = [];
|
||||
|
||||
// If there are too many major ticks to display them all
|
||||
if (numMajorIndices > ticksLimit) {
|
||||
skipMajors(ticks, newTicks, majorIndices, numMajorIndices / ticksLimit);
|
||||
return newTicks;
|
||||
}
|
||||
|
||||
const spacing = calculateSpacing(majorIndices, ticks, ticksLimit);
|
||||
|
||||
if (numMajorIndices > 0) {
|
||||
let i, ilen;
|
||||
const avgMajorSpacing = numMajorIndices > 1 ? Math.round((last - first) / (numMajorIndices - 1)) : null;
|
||||
skip(ticks, newTicks, spacing, isNullOrUndef(avgMajorSpacing) ? 0 : first - avgMajorSpacing, first);
|
||||
for (i = 0, ilen = numMajorIndices - 1; i < ilen; i++) {
|
||||
skip(ticks, newTicks, spacing, majorIndices[i], majorIndices[i + 1]);
|
||||
}
|
||||
skip(ticks, newTicks, spacing, last, isNullOrUndef(avgMajorSpacing) ? ticks.length : last + avgMajorSpacing);
|
||||
return newTicks;
|
||||
}
|
||||
skip(ticks, newTicks, spacing);
|
||||
return newTicks;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {number}
|
||||
* @private
|
||||
|
Loading…
Reference in New Issue
Block a user