@syncfusion/ej2-charts
Version:
Feature-rich chart control with built-in support for over 25 chart types, technical indictors, trendline, zooming, tooltip, selection, crosshair and trackball.
373 lines (372 loc) • 20.8 kB
JavaScript
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
/**
* Accumulation charts base file
*/
import { Animation, animationMode, isNullOrUndefined } from '@syncfusion/ej2-base';
import { stringToNumber, ChartLocation, degreeToLocation, getAnimationFunction, getElement, markerAnimate } from '../../common/utils/helper';
import { Rect } from '@syncfusion/ej2-svg-base';
import { animationComplete } from '../../common/model/constants';
import { AccumulationBase } from './accumulation-base';
/**
* The `PieBase` class is used to perform base calculations for the `Pie` series.
*
* @private
*/
var PieBase = /** @class */ (function (_super) {
__extends(PieBase, _super);
function PieBase() {
return _super !== null && _super.apply(this, arguments) || this;
}
/**
* To initialize the property values.
*
* @private
* @param {AccumulationChart} chart - The accumulation chart control.
* @param {AccumulationSeries} series - The series for which the properties are initialized.
* @returns {void}
*/
PieBase.prototype.initProperties = function (chart, series) {
this.accumulation = chart;
this.size = Math.min(chart.initialClipRect.width, chart.initialClipRect.height);
this.initAngles(series);
var pieCoefficient = 0.8;
var r = parseInt(series.radius, 10);
if ((series.radius.indexOf('%') !== -1 || typeof r === 'number') && !isNaN(r)) {
this.isRadiusMapped = false;
this.pieBaseRadius = stringToNumber(series.radius, series.border.width > 20
? (0.5 * pieCoefficient * this.size) : this.size / 2);
this.innerRadius = stringToNumber(series.innerRadius, this.pieBaseRadius);
this.pieBaseLabelRadius = series.dataLabel.position === 'Inside' ? (((this.pieBaseRadius - this.innerRadius) / 2) + this.innerRadius) :
(this.pieBaseRadius + stringToNumber(series.dataLabel.connectorStyle.length || '4%', this.size / 2));
}
else {
var radiusCollection = [];
this.isRadiusMapped = true;
for (var i = 0; i < Object.keys(series.points).length; i++) {
if (series.points[i].sliceRadius.indexOf('%') !== -1) {
radiusCollection[i] = stringToNumber(series.points[i].sliceRadius, this.size / 2);
}
else {
radiusCollection[i] = parseInt(series.points[i].sliceRadius, 10);
}
}
var minRadius = Math.min.apply(null, radiusCollection);
var maxRadius = Math.max.apply(null, radiusCollection);
this.pieBaseRadius = this.seriesRadius = maxRadius;
this.innerRadius = stringToNumber(series.innerRadius, this.seriesRadius);
this.innerRadius = this.innerRadius > minRadius ? (this.innerRadius / 2) : this.innerRadius;
}
// this.radius = stringToNumber(series.radius, size / 2);
// this.innerRadius = stringToNumber(series.innerRadius, this.radius);
// this.labelRadius = series.dataLabel.position === 'Inside' ? (((this.radius - this.innerRadius) / 2) + this.innerRadius) :
// (this.radius + stringToNumber(series.dataLabel.connectorStyle.length || '4%', size / 2));
this.radius = this.pieBaseRadius;
this.labelRadius = this.pieBaseLabelRadius;
chart.explodeDistance = series.explode ? stringToNumber(series.explodeOffset, this.pieBaseRadius) : 0;
this.findCenter(chart, series);
this.center = this.pieBaseCenter;
if (!chart.redraw) {
this.defaultLabelBound(series, series.dataLabel.visible, series.dataLabel.position);
}
this.totalAngle -= 0.001;
};
/*
* To get label radius of the pie.
* @private
*/
PieBase.prototype.getLabelRadius = function (series, point) {
return series.dataLabel.position === 'Inside' ?
((((stringToNumber(point.sliceRadius, this.pieBaseRadius) - this.innerRadius)) / 2) + this.innerRadius) :
(stringToNumber(point.sliceRadius, this.size / 2) + stringToNumber(series.dataLabel.connectorStyle.length || '4%', this.size / 2));
};
/**
* To find the center of the accumulation.
*
* @private
* @param {AccumulationChart} accumulation - The accumulation chart control.
* @param {AccumulationSeries} series - The series for which the center is calculated.
* @returns {void}
*/
PieBase.prototype.findCenter = function (accumulation, series) {
this.accumulation = accumulation;
this.pieBaseCenter = {
x: stringToNumber(accumulation.center.x, accumulation.initialClipRect.width) + (accumulation.initialClipRect.x),
y: stringToNumber(accumulation.center.y, accumulation.initialClipRect.height) + (accumulation.initialClipRect.y)
};
var accumulationRect = this.getSeriesBound(series);
var accumulationRectCenter = new ChartLocation(accumulationRect.x + accumulationRect.width / 2, accumulationRect.y + accumulationRect.height / 2);
this.pieBaseCenter.x += (this.pieBaseCenter.x - accumulationRectCenter.x);
this.pieBaseCenter.y += (this.pieBaseCenter.y - accumulationRectCenter.y);
this.accumulation.origin = this.pieBaseCenter;
};
/**
* To find angles from series.
*
* @param {AccumulationSeries} series - The series for which to calculate angles.
* @returns {void}
*/
PieBase.prototype.initAngles = function (series) {
var endAngle = isNullOrUndefined(series.endAngle) ? series.startAngle : series.endAngle;
this.totalAngle = (endAngle - series.startAngle) % 360;
this.startAngle = series.startAngle - 90;
this.totalAngle = this.totalAngle <= 0 ? (360 + this.totalAngle) : this.totalAngle;
this.startAngle = (this.startAngle < 0 ? (this.startAngle + 360) : this.startAngle) % 360;
};
/**
* To calculate data-label bound.
*
* @private
* @param {AccumulationSeries} series - The series for which to calculate data-label bounds.
* @param {boolean} visible - Indicates whether the data-labels are visible.
* @param {AccumulationLabelPosition} position - The position of the data-labels.
* @returns {void}
*/
PieBase.prototype.defaultLabelBound = function (series, visible, position) {
var accumulationBound = this.getSeriesBound(series);
series.accumulationBound = accumulationBound;
series.labelBound = new Rect(accumulationBound.x, accumulationBound.y, accumulationBound.width + accumulationBound.x, accumulationBound.height + accumulationBound.y);
if (visible && position === 'Outside') {
series.labelBound = new Rect(Infinity, Infinity, -Infinity, -Infinity);
}
};
/**
* To calculate series bound.
*
* @private
* @param {AccumulationSeries} series - The series for which to calculate the bound.
* @returns {Rect} - Returns a rect.
*/
PieBase.prototype.getSeriesBound = function (series) {
var rect = new Rect(Infinity, Infinity, -Infinity, -Infinity);
this.initAngles(series);
var start = this.startAngle;
var total = this.totalAngle;
var end = (this.startAngle + total) % 360;
end = (end === 0) ? 360 : end;
series.findMaxBounds(rect, this.getRectFromAngle(start));
series.findMaxBounds(rect, this.getRectFromAngle(end));
series.findMaxBounds(rect, new Rect(this.pieBaseCenter.x, this.pieBaseCenter.y, 0, 0));
var nextQuandrant = (Math.floor(start / 90) * 90 + 90) % 360;
var lastQuadrant = (Math.floor(end / 90) * 90) % 360;
lastQuadrant = (lastQuadrant === 0) ? 360 : lastQuadrant;
if (total >= 90 || lastQuadrant === nextQuandrant) {
series.findMaxBounds(rect, this.getRectFromAngle(nextQuandrant));
series.findMaxBounds(rect, this.getRectFromAngle(lastQuadrant));
}
if (start === 0 || (start + total >= 360)) {
series.findMaxBounds(rect, this.getRectFromAngle(0));
}
var length = nextQuandrant === lastQuadrant ? 0 : Math.floor(total / 90);
for (var i = 1; i < length; i++) {
nextQuandrant = nextQuandrant + 90;
if ((nextQuandrant < lastQuadrant || end < start) || total === 360) {
series.findMaxBounds(rect, this.getRectFromAngle(nextQuandrant));
}
}
rect.width -= rect.x;
rect.height -= rect.y;
return rect;
};
/**
* To get rect location size from angle.
*
* @param {number} angle - The angle in degrees.
* @returns {Rect} - The rect representing the location size from angle.
*/
PieBase.prototype.getRectFromAngle = function (angle) {
var location = degreeToLocation(angle, this.pieBaseRadius, this.pieBaseCenter);
return new Rect(location.x, location.y, 0, 0);
};
/**
* To get path arc direction.
*
* @param {ChartLocation} center - The center coordinates of the arc.
* @param {number} start - The starting angle of the arc in degrees.
* @param {number} end - The ending angle of the arc in degrees.
* @param {number} radius - The radius of the arc.
* @param {number} innerRadius - The inner radius of the arc.
* @param {number} borderRadius - The border radius of the arc.
* @param {boolean} isBorder - It specifies whether it is for rendering a border.
* @param {AccPoints[]} seriesPoints - The points of the series.
* @returns {string} - The path string representing the arc direction.
*/
PieBase.prototype.getPathArc = function (center, start, end, radius, innerRadius, borderRadius, isBorder, seriesPoints) {
var degree = end - start;
degree = degree < 0 ? (degree + 360) : degree;
var flag = (degree < 180) ? 0 : 1;
if (!innerRadius && innerRadius === 0) {
return this.getPiePath(center, degreeToLocation(start, radius, center), degreeToLocation(end, radius, center), radius, flag, borderRadius, seriesPoints);
}
else {
return this.getDoughnutPath(center, degreeToLocation(start, radius, center), degreeToLocation(end, radius, center), radius, degreeToLocation(start, innerRadius, center), degreeToLocation(end, innerRadius, center), innerRadius, flag, borderRadius, isBorder, seriesPoints);
}
};
/**
* To get pie direction.
*
* @param {ChartLocation} center - The center of the pie.
* @param {ChartLocation} start - The starting location of the pie.
* @param {ChartLocation} end - The ending location of the pie.
* @param {number} radius - The radius of the pie.
* @param {number} clockWise - The direction of the pie.
* @param {number} cornerRadius - The border radius of the arc.
* @param {AccPoints[]} seriesPoints - The points of the series.
* @returns {string} - The path direction for the pie.
*/
PieBase.prototype.getPiePath = function (center, start, end, radius, clockWise, cornerRadius, seriesPoints) {
var sliceCount = this.sliceCheck(seriesPoints);
cornerRadius = sliceCount === 1 ? 0 : cornerRadius;
var startAngle = Math.atan2(start.y - center.y, start.x - center.x);
var endAngle = Math.atan2(end.y - center.y, end.x - center.x);
cornerRadius = this.adjustCornerRadius(startAngle, endAngle, radius, cornerRadius);
var x1 = start.x - cornerRadius * Math.cos(startAngle);
var y1 = start.y - cornerRadius * Math.sin(startAngle);
var x2 = end.x - cornerRadius * Math.cos(Math.PI / 2 + endAngle);
var y2 = end.y - cornerRadius * Math.sin(Math.PI / 2 + endAngle);
var cx2 = end.x - cornerRadius * Math.cos(endAngle);
var cy2 = end.y - cornerRadius * Math.sin(endAngle);
var cx1 = start.x + cornerRadius * Math.cos(Math.PI / 2 + startAngle);
var cy1 = start.y + cornerRadius * Math.sin(Math.PI / 2 + startAngle);
return "M " + center.x + " " + center.y + " L " + x1 + " " + y1 + " A " + cornerRadius + " " + cornerRadius + " 0 0 1 " + cx1 + " " + cy1 + " A " + radius + " " + radius + " 0 " + clockWise + " 1 " + x2 + " " + y2 + " A " + cornerRadius + " " + cornerRadius + " 0 0 1 " + cx2 + " " + cy2 + " Z";
};
/**
* To get doughnut direction.
*
* @param {ChartLocation} center - The center of the doughnut.
* @param {ChartLocation} start - The starting location of the outer doughnut.
* @param {ChartLocation} end - The ending location of the outer doughnut.
* @param {number} radius - The radius of the outer doughnut.
* @param {ChartLocation} innerStart - The starting location of the inner doughnut.
* @param {ChartLocation} innerEnd - The ending location of the inner doughnut.
* @param {number} innerRadius - The radius of the inner doughnut.
* @param {number} clockWise - The direction of the doughnut.
* @param {number} cornerRadius - The border radius of the arc.
* @param {boolean} isBorder - It specifies whether it is for rendering a border.
* @param {AccPoints[]} seriesPoints - The points of the series.
* @returns {string} - The path direction for the doughnut.
*/
PieBase.prototype.getDoughnutPath = function (center, start, end, radius, innerStart, innerEnd, innerRadius, clockWise, cornerRadius, isBorder, seriesPoints) {
var sliceCount = this.sliceCheck(seriesPoints);
cornerRadius = sliceCount === 1 ? 0 : cornerRadius;
var startAngle = Math.atan2(start.y - innerStart.y, start.x - innerStart.x);
var endAngle = Math.atan2(end.y - innerEnd.y, end.x - innerEnd.x);
cornerRadius = this.adjustCornerRadius(startAngle, endAngle, innerRadius, cornerRadius);
cornerRadius = (isBorder && (this.innerRadius === 0)) ? cornerRadius * -1 : cornerRadius;
var x1 = start.x - cornerRadius * Math.cos(startAngle);
var y1 = start.y - cornerRadius * Math.sin(startAngle);
var x2 = end.x - cornerRadius * Math.cos(Math.PI / 2 + endAngle);
var y2 = end.y - cornerRadius * Math.sin(Math.PI / 2 + endAngle);
var x3 = innerEnd.x + cornerRadius * Math.cos(endAngle);
var y3 = innerEnd.y + cornerRadius * Math.sin(endAngle);
var x4 = innerStart.x + cornerRadius * Math.cos(Math.PI / 2 + startAngle);
var y4 = innerStart.y + cornerRadius * Math.sin(Math.PI / 2 + startAngle);
var cx1 = start.x + cornerRadius * Math.cos(Math.PI / 2 + startAngle);
var cy1 = start.y + cornerRadius * Math.sin(Math.PI / 2 + startAngle);
var cx2 = end.x - cornerRadius * Math.cos(endAngle);
var cy2 = end.y - cornerRadius * Math.sin(endAngle);
var cx3 = innerEnd.x - cornerRadius * Math.cos(Math.PI / 2 + endAngle);
var cy3 = innerEnd.y - cornerRadius * Math.sin(Math.PI / 2 + endAngle);
var cx4 = innerStart.x + cornerRadius * Math.cos(startAngle);
var cy4 = innerStart.y + cornerRadius * Math.sin(startAngle);
if (isBorder) {
return "M " + cx1 + " " + cy1 + " A " + radius + " " + radius + " 0 " + clockWise + " 1 " + x2 + " " + y2 + " L " + cx3 + " " + cy3 + " A " + innerRadius + " " + innerRadius + " 0 " + clockWise + " 0 " + x4 + " " + y4 + " Z";
}
else {
return "M " + x1 + " " + y1 + " A " + cornerRadius + " " + cornerRadius + " 0 0 1 " + cx1 + " " + cy1 + " A " + radius + " " + radius + " 0 " + clockWise + " 1 " + x2 + " " + y2 + " A " + cornerRadius + " " + cornerRadius + " 0 0 1 " + cx2 + " " + cy2 + " L " + x3 + " " + y3 + " A " + cornerRadius + " " + cornerRadius + " 0 0 1 " + cx3 + " " + cy3 + " A " + innerRadius + " " + innerRadius + " 0 " + clockWise + " 0 " + x4 + " " + y4 + " A " + cornerRadius + " " + cornerRadius + " 0 0 1 " + cx4 + " " + cy4 + " Z";
}
};
/**
* Adjusts the corner radius of a pie chart slice based on the angle of the slice.
* Ensures that the corner radius does not exceed a value that would cause the arcs
* of the slice to overlap or create an invalid shape.
*
* @param {number} startAngle - The start angle of the pie.
* @param {number} endAngle - The end angle of the pie.
* @param {number} radius - The radius of the pie.
* @param {number} cornerRadius - The border radius of the arc.
* @returns {number} - The adjusted corner radius of the pie.
*/
PieBase.prototype.adjustCornerRadius = function (startAngle, endAngle, radius, cornerRadius) {
var anglePerSlice = Math.abs(endAngle - startAngle);
if (anglePerSlice > Math.PI) {
anglePerSlice = 2 * Math.PI - anglePerSlice; // Handle large angles that cross the -PI to PI boundary
}
// Adjust corner radius based on the angle per slice
var angleFactor = anglePerSlice / (2 * Math.PI);
var adjustedCornerRadius = radius * angleFactor;
return Math.min(cornerRadius, adjustedCornerRadius);
};
/**
* To Check slice count.
*
* @param {AccPoints[]} seriesPoints - The points of the series.
* @returns {number} - The number of visible pie slice.
*/
PieBase.prototype.sliceCheck = function (seriesPoints) {
var isOneSlice = 0;
for (var index = 0; index < seriesPoints.length; index++) {
var point = seriesPoints[index];
if (point.visible) {
isOneSlice++;
}
}
return isOneSlice;
};
/**
* Method to start animation for pie series.
*
* @param {Element} slice - The slice element to animate.
* @param {AccumulationSeries} series - The accumulation chart control.
* @param {Element} groupElement - The group element containing the pie series.
* @param {number} borderRadius - The border radius of the arc.
* @param {AccPoints[]} seriesPoints - The points of the series.
* @returns {void}
*/
PieBase.prototype.doAnimation = function (slice, series, groupElement, borderRadius, seriesPoints) {
var _this = this;
var startAngle = series.startAngle - 90;
var duration = this.accumulation.duration ? this.accumulation.duration : series.animation.duration;
var value;
this.pieBaseCenter.x += 1;
var radius = Math.max(this.accumulation.availableSize.height, this.accumulation.availableSize.width) * 0.75;
radius += radius * (0.414); // formula r + r / 2 * (1.414 -1)
var effect = getAnimationFunction('Linear'); // need to check animation type
new Animation({}).animate(slice, {
duration: (duration === 0 && animationMode === 'Enable') ? 1000 : duration,
delay: series.animation.delay,
progress: function (args) {
value = effect(args.timeStamp, startAngle, _this.totalAngle, args.duration);
slice.setAttribute('d', _this.getPathArc(_this.pieBaseCenter, startAngle, value, radius, 0, borderRadius, false, seriesPoints));
},
end: function () {
_this.pieBaseCenter.x -= 1;
slice.setAttribute('d', _this.getPathArc(_this.pieBaseCenter, 0, 359.99999, radius, 0, borderRadius, false, seriesPoints));
_this.accumulation.trigger(animationComplete, _this.accumulation.isBlazor ? {} :
{ series: series, accumulation: _this.accumulation, chart: _this.accumulation });
var datalabelGroup = getElement(_this.accumulation.element.id + '_datalabel_Series_' + series.index);
if (datalabelGroup) {
markerAnimate(datalabelGroup, series.animation.delay, series.animation.duration, series, null, null, false);
}
groupElement.style.cssText = '';
var annotationElement = getElement(_this.accumulation.element.id + '_Annotation_Collections');
if (annotationElement) {
annotationElement.style.visibility = 'visible';
}
}
});
};
return PieBase;
}(AccumulationBase));
export { PieBase };