@amcharts/amcharts5
Version:
amCharts 5
513 lines • 20.9 kB
JavaScript
import { Slice } from "../../core/render/Slice";
import { AxisRenderer } from "../xy/axes/AxisRenderer";
import { AxisLabel } from "../xy/axes/AxisLabel";
import { ListTemplate } from "../../core/util/List";
import { Template } from "../../core/util/Template";
import * as $utils from "../../core/util/Utils";
import * as $math from "../../core/util/Math";
import * as $array from "../../core/util/Array";
/**
* Renderer for [[CurveChart]] "horizontal" axes.
*
* @see {@link https://www.amcharts.com/docs/v5/charts/timeline/} for more info
* @since 5.12.0
* @important
*/
export class AxisRendererCurveX extends AxisRenderer {
constructor() {
super(...arguments);
/**
* A list of labels in the axis.
*
* `labels.template` can be used to configure labels.
*
* @default new ListTemplate<AxisLabelRadial>
*/
this.labels = this.addDisposer(new ListTemplate(Template.new({}), () => AxisLabel._new(this._root, {
themeTags: $utils.mergeTags(this.labels.template.get("themeTags", []), this.get("themeTags", []))
}, [this.labels.template])));
/**
* A list of fills in the axis.
*
* `axisFills.template` can be used to configure axis fills.
*
* @default new ListTemplate<Slice>
*/
this.axisFills = this.addDisposer(new ListTemplate(Template.new({}), () => Slice._new(this._root, {
themeTags: $utils.mergeTags(this.axisFills.template.get("themeTags", ["fill"]), this.get("themeTags", []))
}, [this.axisFills.template])));
this.pointPostion = [];
this.pointDistance = [];
this._normalizedPoints = [];
}
_afterNew() {
this._settings.themeTags = $utils.mergeTags(this._settings.themeTags, ["renderer", "circular"]);
super._afterNew();
this.setPrivateRaw("letter", "X");
this._setCRaw("position", "absolute");
}
_beforeChanged() {
super._beforeChanged();
if (this.isDirty("points")) {
this._updateLayout();
}
if (this.isDirty("yRenderer")) {
const yRenderer = this.get("yRenderer");
yRenderer.set("xRenderer", this);
}
if (this.isDirty("rotateLabels")) {
// re-run the label loop so `updateLabel` applies (or clears) rotation
this.axis.markDirtySize();
}
}
/**
* @ignore
*/
processAxis() {
super.processAxis();
const axis = this.axis;
axis.labelsContainer.set("isMeasured", false);
}
/**
* @ignore
*/
_updateLayout() {
const chart = this.chart;
if (chart) {
const points = this.get("points");
if (points) {
// find extreme points
let minX = Infinity;
let minY = Infinity;
let maxX = -Infinity;
let maxY = -Infinity;
let axisLength = 0;
let prevPoint = points[0];
$array.each(points, (point, i) => {
minX = Math.min(minX, point.x);
minY = Math.min(minY, point.y);
maxX = Math.max(maxX, point.x);
maxY = Math.max(maxY, point.y);
axisLength += Math.hypot(point.x - prevPoint.x, point.y - prevPoint.y);
this.pointDistance[i] = axisLength;
prevPoint = point;
});
$array.each(points, (_p, i) => {
this.pointPostion[i] = this.pointDistance[i] / axisLength;
});
let yAxisLenght = this.get("yRenderer").get("axisLength", 0);
let aw = maxX - minX + 2 * yAxisLenght;
let ah = maxY - minY + 2 * yAxisLenght;
// calculate center
let centerX = (minX + maxX) / 2;
let centerY = (minY + maxY) / 2;
// take width and height of a chart
let width = chart.innerWidth();
let height = chart.innerHeight();
// calculate scale
let scaleX = width / aw;
let scaleY = height / ah;
// calculate scale
let scale = Math.min(scaleX, scaleY);
if (!this.getPrivate("autoScale", true)) {
scale = 1;
}
this.axis.setPrivateRaw("width", aw * scale);
this.axis.setPrivateRaw("height", ah * scale);
this.setPrivateRaw("centerX", centerX);
this.setPrivateRaw("centerY", centerY);
this.setPrivateRaw("scale", scale);
this.setPrivateRaw("axisLength", axisLength);
this.set("draw", (display) => {
display.moveTo((points[0].x - centerX) * scale, (points[0].y - centerY) * scale);
$array.each(points, (point) => {
display.lineTo((point.x - centerX) * scale, (point.y - centerY) * scale);
});
});
prevPoint = points[0];
this._normalizedPoints = [];
$array.each(points, (point) => {
let distance = Math.hypot(point.x - prevPoint.x, point.y - prevPoint.y);
for (let i = 1; i < distance; i++) {
let x = ((prevPoint.x + (point.x - prevPoint.x) * i / distance) - centerX) * scale;
let y = ((prevPoint.y + (point.y - prevPoint.y) * i / distance) - centerY) * scale;
// check distance to previous normalized point
const prevNormalized = this._normalizedPoints[this._normalizedPoints.length - 1];
if (prevNormalized) {
let distance = Math.hypot(x - prevNormalized.x, y - prevNormalized.y);
if (distance < .5) {
continue;
}
}
this._normalizedPoints.push({ x: x, y: y });
}
prevPoint = point;
});
this.axis.markDirtySize();
}
}
}
/**
* @ignore
*/
updateGrid(grid, position, endPosition) {
if (grid) {
if (position == null) {
position = 0;
}
let location = grid.get("location", 0.5);
if (endPosition != null && endPosition != position) {
position = position + (endPosition - position) * location;
}
this.toggleVisibility(grid, position, 0, 1);
const p0 = this.positionToPoint(position, 0);
const p1 = this.positionToPoint(position, 1);
grid.set("draw", (display) => {
display.moveTo(p0.x, p0.y);
display.lineTo(p1.x, p1.y);
});
}
}
/**
* Converts relative position to angle.
*
* @param position Position
* @return Angle
*/
positionToAngle(position) {
position = this.toGlobalPosition(position);
let points = this.get("points");
let axisLength = this.getPrivate("axisLength", 0);
let angle = 0;
// find which segment the position is in
if (points) {
let totalLength = 0;
let prevPoint = points[0];
$array.eachContinue(points, (point) => {
let segmentLength = Math.hypot(point.x - prevPoint.x, point.y - prevPoint.y);
totalLength += segmentLength;
if (totalLength > position * axisLength) {
angle = $math.getAngle(prevPoint, point) + 90;
return false;
}
prevPoint = point;
return true;
});
}
return angle;
}
getPoints(positionX, positionY, endPositionX, endPositionY) {
let yRenderer = this.get("yRenderer");
let points = [];
if (positionX > endPositionX) {
[positionX, endPositionX] = [endPositionX, positionX];
}
if (positionY > endPositionY) {
[positionY, endPositionY] = [endPositionY, positionY];
}
if (yRenderer) {
let indexStart = this.positionToIndex(positionX);
let indexEnd = this.positionToIndex(endPositionX);
points.push(this.positionToPoint(positionX, positionY));
for (let i = indexStart; i <= indexEnd + 1; i++) {
let position = this.indexToPosition(i);
if (position > positionX && position < endPositionX) {
points.push(this.positionToPoint(position, positionY));
}
}
points.push(this.positionToPoint(endPositionX, positionY));
if (endPositionY != positionY) {
points.push(this.positionToPoint(endPositionX, endPositionY));
for (let i = indexEnd + 1; i >= indexStart; i--) {
let position = this.indexToPosition(i);
if (position > positionX && position < endPositionX) {
points.push(this.positionToPoint(position, endPositionY));
}
}
points.push(this.positionToPoint(positionX, endPositionY));
points.push(points[0]);
}
}
return points;
}
// do not delete
_handleOpposite() { }
positionToIndex(position) {
let points = this.get("points");
let axisLength = this.getPrivate("axisLength", 0);
let index = 0;
position = this.toGlobalPosition(position);
if (points && points.length > 1) {
let totalLength = 0;
let prevPoint = points[0];
if (position <= 0) {
index = 0;
}
else if (position >= 1) {
const len = points.length;
index = len - 1;
}
else {
let i = 0;
$array.eachContinue(points, (point) => {
let segmentLength = Math.hypot(point.x - prevPoint.x, point.y - prevPoint.y);
totalLength += segmentLength;
if (totalLength >= position * axisLength) {
index = i;
return false;
}
prevPoint = point;
i++;
return true;
});
}
}
return index;
}
indexToPosition(index) {
let points = this.get("points");
let axisLength = this.getPrivate("axisLength", 0);
let position = 0;
if (points && points.length > 1) {
let totalLength = 0;
let prevPoint = points[0];
let i = 0;
$array.eachContinue(points, (point) => {
position = totalLength / axisLength;
if (i == index) {
return false;
}
let segmentLength = Math.hypot(point.x - prevPoint.x, point.y - prevPoint.y);
totalLength += segmentLength;
prevPoint = point;
i++;
return true;
});
}
return this.toAxisPosition(position);
}
pointToPosition(point) {
let minDistance = Infinity;
let index = 0;
let prevPoint = this._normalizedPoints[0];
let angle = 0;
$array.each(this._normalizedPoints, (normalizedPoint, i) => {
let distance = Math.hypot(normalizedPoint.x - point.x, normalizedPoint.y - point.y);
if (distance < minDistance) {
minDistance = distance;
index = i;
angle = $math.getAngle(point, normalizedPoint) - $math.getAngle(prevPoint, normalizedPoint);
}
prevPoint = normalizedPoint;
});
const yRenderer = this.get("yRenderer");
const scale = this.getPrivate("scale", 1);
const rendererY = this.get("yRenderer");
const y = -minDistance / (yRenderer.get("axisLength", 0) * scale) * $math.sin(angle) + rendererY.get("axisLocation", 0);
return { x: index / this._normalizedPoints.length, y: y };
}
/**
* Converts relative position to an X/Y coordinate.
*
* @param position Position
* @return Point
*/
positionToPoint(position, positionY, doNotFix) {
if (positionY == undefined) {
positionY = 0;
}
const rendererY = this.get("yRenderer");
if (!doNotFix) {
position = this.toGlobalPosition(position);
}
positionY = rendererY.toGlobalPosition(positionY);
positionY -= rendererY.get("axisLocation", 0);
let points = this.get("points");
let axisLength = this.getPrivate("axisLength", 0);
let scale = this.getPrivate("scale", 1);
let pointOfPosition = { x: 0, y: 0 };
let angle = 0;
// find which segment the position is in
if (points && points.length > 1) {
let totalLength = 0;
let prevPoint = points[0];
if (position <= 0) {
angle = $math.getAngle(points[0], points[1]) + 90;
pointOfPosition = points[0];
}
else if (position >= 1) {
const len = points.length;
angle = $math.getAngle(points[len - 2], points[len - 1]) + 90;
pointOfPosition = points[len - 1];
}
else {
$array.eachContinue(points, (point) => {
let segmentLength = Math.hypot(point.x - prevPoint.x, point.y - prevPoint.y);
totalLength += segmentLength;
if (totalLength >= position * axisLength) {
let segementStartPosition = (totalLength - segmentLength) / axisLength;
let segmentPosition = (position * axisLength - segementStartPosition * axisLength) / segmentLength;
pointOfPosition = { x: prevPoint.x + (point.x - prevPoint.x) * segmentPosition, y: prevPoint.y + (point.y - prevPoint.y) * segmentPosition };
angle = $math.getAngle(prevPoint, point) + 90;
return false;
}
prevPoint = point;
return true;
});
}
}
let centerX = this.getPrivate("centerX", 0);
let centerY = this.getPrivate("centerY", 0);
let axisX = (pointOfPosition.x - centerX) * scale;
let axisY = (pointOfPosition.y - centerY) * scale;
if (positionY == null) {
positionY = 0;
}
let lengthY = -rendererY.get("axisLength", 0);
let dy = lengthY * positionY * scale;
let x = axisX + $math.cos(angle) * dy;
let y = axisY + $math.sin(angle) * dy;
return { x: x, y: y };
}
/**
* @ignore
*/
updateLabel(label, position, endPosition, count) {
if (label) {
if (position == null) {
position = 0;
}
let location = 0.5;
if (count != null && count > 1) {
location = label.get("multiLocation", location);
}
else {
location = label.get("location", location);
}
if (endPosition != null && endPosition != position) {
position = position + (endPosition - position) * location;
}
const angle = this.positionToAngle(position);
const rendererY = this.get("yRenderer");
const axisY = rendererY.axis;
const point = this.positionToPoint(position, axisY.get("start", 0) + rendererY.get("axisLocation", 0));
label.setAll({
x: point.x,
y: point.y
});
if (this.get("rotateLabels", true)) {
label.set("rotation", angle - 90);
}
else {
// reset, so turning `rotateLabels` off again unrotates the labels
label.set("rotation", 0);
}
this.toggleVisibility(label, position, label.get("minPosition", 0), label.get("maxPosition", 1));
}
}
/**
* @ignore
*/
updateTick(tick, position, endPosition, count) {
if (tick) {
if (position == null) {
position = 0;
}
let location = 0.5;
if (count != null && count > 1) {
location = tick.get("multiLocation", location);
}
else {
location = tick.get("location", location);
}
if (endPosition != null && endPosition != position) {
position = position + (endPosition - position) * location;
}
let length = tick.get("length", 0);
const inside = tick.get("inside");
if (inside) {
length *= -1;
}
let angle = this.positionToAngle(position);
this.toggleVisibility(tick, position, tick.get("minPosition", 0), tick.get("maxPosition", 1));
const rendererY = this.get("yRenderer");
const axisY = rendererY.axis;
const point = this.positionToPoint(position, axisY.get("start"));
tick.set("draw", (display) => {
display.moveTo(point.x, point.y);
display.lineTo(point.x - length * $math.cos(angle), point.y - length * $math.sin(angle));
});
}
}
/**
* @ignore
*/
updateBullet(bullet, position, endPosition) {
if (bullet) {
const sprite = bullet.get("sprite");
if (sprite) {
if (position == null) {
position = 0;
}
let location = bullet.get("location", 0.5);
if (endPosition != null && endPosition != position) {
position = position + (endPosition - position) * location;
}
const rendererY = this.get("yRenderer");
const axisY = rendererY.axis;
const point = this.positionToPoint(position, axisY.get("start"));
sprite.setAll({ x: point.x, y: point.y });
this.toggleVisibility(sprite, position, 0, 1);
}
}
}
/**
* @ignore
*/
updateFill(fill, position, endPosition) {
if (fill) {
if (position == null) {
position = 0;
}
if (endPosition == null) {
endPosition = 1;
}
const yRenderer = this.get("yRenderer");
const yAxis = yRenderer.axis;
const points = this.getPoints(position, yAxis.get("start", 0), endPosition, yAxis.get("end", 1));
fill.set("draw", (display) => {
display.moveTo(points[0].x, points[0].y);
$array.each(points, (point) => {
display.lineTo(point.x, point.y);
});
display.closePath();
});
}
}
/**
* Returns axis length in pixels.
*
* @return Length
*/
axisLength() {
return this.getPrivate("axisLength", 0);
}
/**
* @ignore
*/
positionTooltip(tooltip, position) {
const yRenderer = this.get("yRenderer");
const start = yRenderer.axis.get("start", 0);
const end = yRenderer.axis.get("end", 1);
const point = this.positionToPoint(position, yRenderer.get("axisLocation", 0.5) * (end - start) + start);
this._positionTooltip(tooltip, point);
}
/**
* @ignore
*/
updateTooltipBounds(_tooltip) {
}
}
AxisRendererCurveX.className = "AxisRendererCurveX";
AxisRendererCurveX.classNames = AxisRenderer.classNames.concat([AxisRendererCurveX.className]);
//# sourceMappingURL=AxisRendererCurveX.js.map