| `display` | `boolean` | `true` | Is the legend shown?
| `position` | `string` | `'top'` | Position of the legend. [more...](#position)
| `align` | `string` | `'center'` | Alignment of the legend. [more...](#align)
+| `maxHeight` `number` | | Maximum height of the legend, in pixels
+| `maxWidth` `number` | | Maximum width of the legend, in pixels
| `fullWidth` | `boolean` | `true` | Marks that this box should take the full width of the canvas (pushing down other boxes). This is unlikely to need to be changed in day-to-day use.
| `onClick` | `function` | | A callback that is called when a click event is registered on a label item. Arguments: `[event, legendItem, legend]`.
| `onHover` | `function` | | A callback that is called when a 'mousemove' event is registered on top of a label item. Arguments: `[event, legendItem, legend]`.
import DatasetController from '../core/core.datasetController';
import {isArray, valueOrDefault} from '../helpers/helpers.core';
-import {toRadians} from '../helpers/helpers.math';
+import {toRadians, PI, TAU, HALF_PI} from '../helpers/helpers.math';
/**
* @typedef { import("../core/core.controller").default } Chart
*/
-const PI = Math.PI;
-const DOUBLE_PI = PI * 2;
-const HALF_PI = PI / 2;
function getRatioAndOffset(rotation, circumference, cutout) {
let ratioX = 1;
let offsetX = 0;
let offsetY = 0;
// If the chart's circumference isn't a full circle, calculate size as a ratio of the width/height of the arc
- if (circumference < DOUBLE_PI) {
- let startAngle = rotation % DOUBLE_PI;
- startAngle += startAngle >= PI ? -DOUBLE_PI : startAngle < -PI ? DOUBLE_PI : 0;
+ if (circumference < TAU) {
+ let startAngle = rotation % TAU;
+ startAngle += startAngle >= PI ? -TAU : startAngle < -PI ? TAU : 0;
const endAngle = startAngle + circumference;
const startX = Math.cos(startAngle);
const startY = Math.sin(startAngle);
const endX = Math.cos(endAngle);
const endY = Math.sin(endAngle);
- const contains0 = (startAngle <= 0 && endAngle >= 0) || endAngle >= DOUBLE_PI;
- const contains90 = (startAngle <= HALF_PI && endAngle >= HALF_PI) || endAngle >= DOUBLE_PI + HALF_PI;
+ const contains0 = (startAngle <= 0 && endAngle >= 0) || endAngle >= TAU;
+ const contains90 = (startAngle <= HALF_PI && endAngle >= HALF_PI) || endAngle >= TAU + HALF_PI;
const contains180 = startAngle === -PI || endAngle >= PI;
const contains270 = (startAngle <= -HALF_PI && endAngle >= -HALF_PI) || endAngle >= PI + HALF_PI;
const minX = contains180 ? -1 : Math.min(startX, startX * cutout, endX, endX * cutout);
* across all visible datasets.
*/
_getRotationExtents() {
- let min = DOUBLE_PI;
- let max = -DOUBLE_PI;
+ let min = TAU;
+ let max = -TAU;
const me = this;
const opts = me.chart.options;
const opts = me.chart.options;
const meta = me._cachedMeta;
const circumference = toRadians(valueOrDefault(me._config.circumference, opts.circumference));
- return reset && opts.animation.animateRotate ? 0 : this.chart.getDataVisibility(i) ? me.calculateCircumference(meta._parsed[i] * circumference / DOUBLE_PI) : 0;
+ return reset && opts.animation.animateRotate ? 0 : this.chart.getDataVisibility(i) ? me.calculateCircumference(meta._parsed[i] * circumference / TAU) : 0;
}
updateElements(arcs, start, count, mode) {
calculateCircumference(value) {
const total = this._cachedMeta.total;
if (total > 0 && !isNaN(value)) {
- return DOUBLE_PI * (Math.abs(value) / total);
+ return TAU * (Math.abs(value) / total);
}
return 0;
}
import DatasetController from '../core/core.datasetController';
-import {toRadians} from '../helpers/helpers.math';
-import {resolve} from '../helpers/helpers.options';
+import {resolve, toRadians, PI, TAU} from '../helpers/index';
function getStartAngleRadians(deg) {
// radialLinear scale draws angleLines using startAngle. 0 is expected to be at top.
// Here we adjust to standard unit circle used in drawing, where 0 is at right.
- return toRadians(deg) - 0.5 * Math.PI;
+ return toRadians(deg) - 0.5 * PI;
}
export default class PolarAreaController extends DatasetController {
return resolve([
me.chart.options.elements.arc.angle,
- (2 * Math.PI) / count
+ TAU / count
], context, index);
}
}
import Element from './core.element';
import {_alignPixel, _measureText} from '../helpers/helpers.canvas';
import {callback as call, each, isArray, isFinite, isNullOrUndef, isObject, valueOrDefault} from '../helpers/helpers.core';
-import {_factorize, toDegrees, toRadians, _int16Range} from '../helpers/helpers.math';
+import {_factorize, toDegrees, toRadians, _int16Range, HALF_PI} from '../helpers/helpers.math';
import {toFont, resolve, toPadding} from '../helpers/helpers.options';
import Ticks from './core.ticks';
scaleLabelY = me.top + me.height / 2;
textAlign = 'center';
}
- rotation = isLeft ? -0.5 * Math.PI : 0.5 * Math.PI;
+ rotation = isLeft ? -HALF_PI : HALF_PI;
}
ctx.save();
import Element from '../core/core.element';
-import {_angleBetween, getAngleFromPoint} from '../helpers/helpers.math';
-
-const TAU = Math.PI * 2;
+import {_angleBetween, getAngleFromPoint, TAU, HALF_PI} from '../helpers/index';
function clipArc(ctx, element) {
const {startAngle, endAngle, pixelMargin, x, y, outerRadius, innerRadius} = element;
angleMargin = pixelMargin / innerRadius;
ctx.arc(x, y, innerRadius, endAngle + angleMargin, startAngle - angleMargin, true);
} else {
- ctx.arc(x, y, pixelMargin, endAngle + Math.PI / 2, startAngle - Math.PI / 2);
+ ctx.arc(x, y, pixelMargin, endAngle + HALF_PI, startAngle - HALF_PI);
}
ctx.closePath();
ctx.clip();
-import {isArray, isNullOrUndef} from './helpers.core';
+import {
+ isArray, isNullOrUndef, PI, TAU, HALF_PI, QUARTER_PI,
+ TWO_THIRDS_PI, RAD_PER_DEG
+} from './index';
/**
* @typedef { import("../core/core.controller").default } Chart
*/
-const PI = Math.PI;
-const RAD_PER_DEG = PI / 180;
-const DOUBLE_PI = PI * 2;
-const HALF_PI = PI / 2;
-const QUARTER_PI = PI / 4;
-const TWO_THIRDS_PI = PI * 2 / 3;
-
/**
* @namespace Chart.helpers.canvas
*/
switch (style) {
// Default includes circle
default:
- ctx.arc(x, y, radius, 0, DOUBLE_PI);
+ ctx.arc(x, y, radius, 0, TAU);
ctx.closePath();
break;
case 'triangle':
+import {INFINITY} from './index';
+
/**
* @private
*/
};
}
-const infinity = Number.POSITIVE_INFINITY;
-
function getContainerSize(canvas, width, height) {
let maxWidth, maxHeight;
return {
width,
height,
- maxWidth: maxWidth || infinity,
- maxHeight: maxHeight || infinity
+ maxWidth: maxWidth || INFINITY,
+ maxHeight: maxHeight || INFINITY
};
}
export function getMaximumSize(canvas, bbWidth, bbHeight, aspectRatio) {
const style = getComputedStyle(canvas);
const margins = getPositionedStyle(style, 'margin');
- const maxWidth = parseMaxStyle(style.maxWidth, canvas, 'clientWidth') || infinity;
- const maxHeight = parseMaxStyle(style.maxHeight, canvas, 'clientHeight') || infinity;
+ const maxWidth = parseMaxStyle(style.maxWidth, canvas, 'clientWidth') || INFINITY;
+ const maxHeight = parseMaxStyle(style.maxHeight, canvas, 'clientHeight') || INFINITY;
const containerSize = getContainerSize(canvas, bbWidth, bbHeight);
let {width, height} = containerSize;
+import {PI, TAU, HALF_PI} from './index';
+
/**
* Easing functions adapted from Robert Penner's easing equations.
* @namespace Chart.helpers.easing.effects
},
easeInSine(t) {
- return -Math.cos(t * (Math.PI / 2)) + 1;
+ return -Math.cos(t * HALF_PI) + 1;
},
easeOutSine(t) {
- return Math.sin(t * (Math.PI / 2));
+ return Math.sin(t * HALF_PI);
},
easeInOutSine(t) {
- return -0.5 * (Math.cos(Math.PI * t) - 1);
+ return -0.5 * (Math.cos(PI * t) - 1);
},
easeInExpo(t) {
a = 1;
s = p / 4;
} else {
- s = p / (2 * Math.PI) * Math.asin(1 / a);
+ s = p / TAU * Math.asin(1 / a);
}
- return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p));
+ return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) * TAU / p));
},
easeOutElastic(t) {
a = 1;
s = p / 4;
} else {
- s = p / (2 * Math.PI) * Math.asin(1 / a);
+ s = p / TAU * Math.asin(1 / a);
}
- return a * Math.pow(2, -10 * t) * Math.sin((t - s) * (2 * Math.PI) / p) + 1;
+ return a * Math.pow(2, -10 * t) * Math.sin((t - s) * TAU / p) + 1;
},
easeInOutElastic(t) {
a = 1;
s = p / 4;
} else {
- s = p / (2 * Math.PI) * Math.asin(1 / a);
+ s = p / TAU * Math.asin(1 / a);
}
if (t < 1) {
- return -0.5 * (a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p));
+ return -0.5 * (a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) * TAU / p));
}
- return a * Math.pow(2, -10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p) * 0.5 + 1;
+ return a * Math.pow(2, -10 * (t -= 1)) * Math.sin((t - s) * TAU / p) * 0.5 + 1;
},
easeInBack(t) {
const s = 1.70158;
import {isFinite as isFiniteNumber} from './helpers.core';
-const PI = Math.PI;
-const TAU = 2 * PI;
-const PITAU = TAU + PI;
+export const PI = Math.PI;
+export const TAU = 2 * PI;
+export const PITAU = TAU + PI;
+export const INFINITY = Number.POSITIVE_INFINITY;
+export const RAD_PER_DEG = PI / 180;
+export const HALF_PI = PI / 2;
+export const QUARTER_PI = PI / 4;
+export const TWO_THIRDS_PI = PI * 2 / 3;
/**
* @alias Chart.helpers.math
import {_boundSegment, _boundSegments} from '../helpers/helpers.segment';
import {clipArea, unclipArea} from '../helpers/helpers.canvas';
import {isArray, isFinite, valueOrDefault} from '../helpers/helpers.core';
-import {_normalizeAngle} from '../helpers/helpers.math';
+import {TAU, _normalizeAngle} from '../helpers/helpers.math';
/**
* @typedef { import('../core/core.controller').default } Chart
pathSegment(ctx, bounds, opts) {
const {x, y, radius} = this;
- bounds = bounds || {start: 0, end: Math.PI * 2};
+ bounds = bounds || {start: 0, end: TAU};
if (opts.reverse) {
ctx.arc(x, y, radius, bounds.end, bounds.start, true);
} else {
import Element from '../core/core.element';
import layouts from '../core/core.layouts';
import {drawPoint} from '../helpers/helpers.canvas';
-import {callback as call, merge, valueOrDefault, isNullOrUndef} from '../helpers/helpers.core';
-import {toFont, toPadding} from '../helpers/helpers.options';
-import {getRtlAdapter, overrideTextDirection, restoreTextDirection} from '../helpers/helpers.rtl';
+import {
+ callback as call, merge, valueOrDefault, isNullOrUndef, toFont,
+ toPadding, getRtlAdapter, overrideTextDirection, restoreTextDirection,
+ INFINITY
+} from '../helpers/index';
/**
* @typedef { import("../platform/platform.base").IEvent } IEvent
minSize.width += totalWidth;
}
- me.width = minSize.width;
- me.height = minSize.height;
+ me.width = Math.min(minSize.width, opts.maxWidth || INFINITY);
+ me.height = Math.min(minSize.height, opts.maxHeight || INFINITY);
}
afterFit() {}
import defaults from '../core/core.defaults';
import Element from '../core/core.element';
import layouts from '../core/core.layouts';
-import {isArray, mergeIf} from '../helpers/helpers.core';
-import {toPadding, toFont} from '../helpers/helpers.options';
+import {PI, isArray, mergeIf, toPadding, toFont} from '../helpers';
export class Title extends Element {
constructor(config) {
break;
}
maxWidth = bottom - top;
- rotation = Math.PI * (opts.position === 'left' ? -0.5 : 0.5);
+ rotation = PI * (opts.position === 'left' ? -0.5 : 0.5);
}
ctx.save();
import defaults from '../core/core.defaults';
import {_longestText} from '../helpers/helpers.canvas';
-import {isNumber, toDegrees, toRadians, _normalizeAngle} from '../helpers/helpers.math';
+import {HALF_PI, isNumber, TAU, toDegrees, toRadians, _normalizeAngle} from '../helpers/helpers.math';
import LinearScaleBase from './scale.linearbase';
import Ticks from '../core/core.ticks';
import {valueOrDefault, isArray, isFinite, callback as callCallback, isNullOrUndef} from '../helpers/helpers.core';
ctx.beginPath();
if (circular) {
// Draw circular arcs between the points
- ctx.arc(scale.xCenter, scale.yCenter, radius, 0, Math.PI * 2);
+ ctx.arc(scale.xCenter, scale.yCenter, radius, 0, TAU);
} else {
// Draw straight lines connecting each index
pointPosition = scale.getPointPosition(0, radius);
getIndexAngle(index) {
const chart = this.chart;
- const angleMultiplier = Math.PI * 2 / chart.data.labels.length;
+ const angleMultiplier = TAU / chart.data.labels.length;
const options = chart.options || {};
const startAngle = options.startAngle || 0;
getPointPosition(index, distanceFromCenter) {
const me = this;
- const angle = me.getIndexAngle(index) - (Math.PI / 2);
+ const angle = me.getIndexAngle(index) - HALF_PI;
return {
x: Math.cos(angle) * distanceFromCenter + me.xCenter,
y: Math.sin(angle) * distanceFromCenter + me.yCenter,