@amcharts/amcharts5
Version:
amCharts 5
574 lines • 25.5 kB
JavaScript
import { XYSeries } from "./XYSeries";
import { Graphics } from "../../../core/render/Graphics";
import { line, area } from "d3-shape";
import { Template } from "../../../core/util/Template";
import { ListTemplate } from "../../../core/util/List";
import { color } from "../../../core/util/Color";
import { DataItem } from "../../../core/render/Component";
import { Rectangle } from "../../../core/render/Rectangle";
import * as $type from "../../../core/util/Type";
import * as $array from "../../../core/util/Array";
import * as $utils from "../../../core/util/Utils";
/**
* Used to plot line and/or area series.
*
* @see {@link https://www.amcharts.com/docs/v5/charts/xy-chart/series/line-series/} for more info
* @important
*/
export class LineSeries extends XYSeries {
constructor() {
super(...arguments);
this._strokeGenerator = line();
this._fillGenerator = area();
/**
* A [[TemplateList]] of all line segments in series.
*
* `strokes.template` can be used to set default settings for all line
* segments, or to change on existing ones.
*
* @default new ListTemplate<Graphics>
*/
this.strokes = this.addDisposer(new ListTemplate(Template.new({}), () => Graphics._new(this._root, {
themeTags: $utils.mergeTags(this.strokes.template.get("themeTags", []), ["line", "series", "stroke"])
}, [this.strokes.template])));
/**
* A [[TemplateList]] of all segment fills in series.
*
* `fills.template` can be used to set default settings for all segment
* fills, or to change on existing ones.
*
* @default new ListTemplate<Graphics>
*/
this.fills = this.addDisposer(new ListTemplate(Template.new({}), () => Graphics._new(this._root, {
themeTags: $utils.mergeTags(this.strokes.template.get("themeTags", []), ["line", "series", "fill"])
}, [this.fills.template])));
this._previousPoint = [0, 0, 0, 0];
this._dindex = 0;
this._sindex = 0;
}
_afterNew() {
this._fillGenerator.y0(function (p) {
return p[3];
});
this._fillGenerator.x0(function (p) {
return p[2];
});
this._fillGenerator.y1(function (p) {
return p[1];
});
this._fillGenerator.x1(function (p) {
return p[0];
});
super._afterNew();
}
/**
* @ignore
*/
makeStroke(strokes) {
const stroke = this.mainContainer.children.push(strokes.make());
strokes.push(stroke);
return stroke;
}
/**
* @ignore
*/
makeFill(fills) {
const fill = this.mainContainer.children.push(fills.make());
fills.push(fill);
return fill;
}
_updateChildren() {
this._strokeTemplate = undefined;
this._fillTemplate = undefined;
let xAxis = this.get("xAxis");
let yAxis = this.get("yAxis");
if (this.isDirty("stroke")) {
const stroke = this.get("stroke");
this.strokes.template._setC("stroke", stroke);
const legendStroke = this._legendStroke;
if (legendStroke) {
legendStroke.states.lookup("default")._setC("stroke", stroke);
}
}
if (this.isDirty("fill")) {
const fill = this.get("fill");
this.fills.template._setC("fill", fill);
const legendFill = this._legendFill;
if (legendFill) {
legendFill.states.lookup("default")._setC("fill", fill);
}
}
if (this.isDirty("fillPattern")) {
const fillPattern = this.get("fillPattern");
this.fills.template._setC("fillPattern", fillPattern);
const legendFill = this._legendFill;
if (legendFill) {
legendFill.states.lookup("default")._setC("fillPattern", fillPattern);
}
}
if (this.isDirty("curveFactory")) {
const curveFactory = this.get("curveFactory");
if (curveFactory) {
this._strokeGenerator.curve(curveFactory);
this._fillGenerator.curve(curveFactory);
}
}
if (xAxis.inited && yAxis.inited) {
if (this._axesDirty || this._valuesDirty || this._stackDirty || this.isDirty("vcx") || this.isDirty("vcy") || this._sizeDirty || this.isDirty("connect") || this.isDirty("curveFactory")) {
const dataItems = this.dataItems;
this.fills.each((fill) => {
fill.setPrivate("visible", false);
});
this.strokes.each((stroke) => {
stroke.setPrivate("visible", false);
});
this.axisRanges.each((axisRange) => {
let fills = axisRange.fills;
if (fills) {
fills.each((fill) => {
fill.setPrivate("visible", false);
});
}
let strokes = axisRange.strokes;
if (strokes) {
strokes.each((stroke) => {
stroke.setPrivate("visible", false);
});
}
});
let startIndex = this.startIndex();
let strokeTemplateField = this.strokes.template.get("templateField");
let fillTemplateField = this.fills.template.get("templateField");
let strokeTemplateFound = true;
let fillTemplateFound = true;
if (strokeTemplateField) {
strokeTemplateFound = false;
}
if (fillTemplateField) {
fillTemplateFound = false;
}
for (let i = startIndex - 1; i >= 0; i--) {
let dataItem = dataItems[i];
let hasValues = true;
let dataContext = dataItem.dataContext;
if (strokeTemplateField) {
if (dataContext[strokeTemplateField]) {
strokeTemplateFound = true;
}
}
if (fillTemplateField) {
if (dataContext[fillTemplateField]) {
fillTemplateFound = true;
}
}
for (let f = 0, flen = this._valueFields.length; f < flen; f++) {
if (!$type.isNumber(dataItem.get(this._valueFields[f]))) {
hasValues = false;
break;
}
}
if (hasValues && strokeTemplateFound && fillTemplateFound) {
startIndex = i;
break;
}
}
let len = dataItems.length;
let endIndex = this.endIndex();
if (endIndex < len) {
endIndex++;
for (let i = endIndex; i < len; i++) {
let dataItem = dataItems[i];
let hasValues = true;
for (let f = 0, flen = this._valueFields.length; f < flen; f++) {
if (!$type.isNumber(dataItem.get(this._valueFields[f]))) {
hasValues = false;
break;
}
}
if (hasValues) {
endIndex = i + 1;
break;
}
}
}
if (startIndex > 0) {
startIndex--;
}
this._endIndex = endIndex;
this._clearGraphics();
this._sindex = 0;
this._dindex = startIndex;
if (len == 1) {
this._startSegment(0);
}
else {
// this is done to avoid recursion with a lot of segments
while (this._dindex < endIndex - 1) {
this._startSegment(this._dindex);
this._sindex++;
}
}
}
}
else {
this._skipped = true;
}
super._updateChildren();
}
_clearGraphics() {
this.strokes.clear();
this.fills.clear();
this.axisRanges.each((axisRange) => {
axisRange.fills.clear();
axisRange.strokes.clear();
});
}
_startSegment(dataItemIndex) {
const dataItems = this.dataItems;
let endIndex = this._endIndex;
let currentEndIndex = endIndex;
const autoGapCount = this.get("autoGapCount");
const connect = this.get("connect");
const fill = this.makeFill(this.fills);
const fillTemplate = this._fillTemplate;
const originalTemplate = this.fills.template;
if (fillTemplate && fillTemplate != originalTemplate) {
fill.template = fillTemplate;
}
fill.setPrivate("visible", true);
const stroke = this.makeStroke(this.strokes);
const strokeTemplate = this._strokeTemplate;
if (strokeTemplate && strokeTemplate != this.strokes.template) {
stroke.template = strokeTemplate;
}
stroke.setPrivate("visible", true);
let xAxis = this.get("xAxis");
let yAxis = this.get("yAxis");
let baseAxis = this.get("baseAxis");
let vcx = this.get("vcx", 1);
let vcy = this.get("vcy", 1);
let xField = this._xField;
let yField = this._yField;
let xOpenField = this._xOpenField;
let yOpenField = this._yOpenField;
const xOpenFieldValue = this.get("openValueXField");
const yOpenFieldValue = this.get("openValueYField");
if (!xOpenFieldValue) {
xOpenField = this._xField;
}
if (!yOpenFieldValue) {
yOpenField = this._yField;
}
const stacked = this.get("stacked");
const basePosX = xAxis.basePosition();
const basePosY = yAxis.basePosition();
let baseField;
if (baseAxis === yAxis) {
baseField = this._yField;
}
else {
baseField = this._xField;
}
const segments = [];
let points = [];
segments.push(points);
const strokeTemplateField = this.strokes.template.get("templateField");
const fillTemplateField = this.fills.template.get("templateField");
let locationX = this.get("locationX", 0.5);
let locationY = this.get("locationY", 0.5);
let openLocationX = this.get("openLocationX", locationX);
let openLocationY = this.get("openLocationY", locationY);
const minDistance = this.get("minDistance", 0);
let i;
let fillVisible = this.fills.template.get("visible");
if (this.axisRanges.length > 0) {
fillVisible = true;
}
let getOpen = false;
if (stacked || xOpenFieldValue || yOpenFieldValue) {
getOpen = true;
}
const o = {
points, segments, stacked, getOpen, basePosX, basePosY, fillVisible, xField, yField, xOpenField, yOpenField, vcx, vcy, baseAxis, xAxis, yAxis, locationX, locationY, openLocationX, openLocationY, minDistance
};
let rangeStrokeTemplate = this._strokeTemplate;
let rangeFillTemplate = this._fillTemplate;
for (i = dataItemIndex; i < currentEndIndex; i++) {
this._dindex = i;
const dataItem = dataItems[i];
let valueX = dataItem.get(xField);
let valueY = dataItem.get(yField);
if (valueX == null || valueY == null) {
if (!connect) {
points = [];
segments.push(points);
o.points = points;
}
}
else {
this._getPoints(dataItem, o);
}
if (strokeTemplateField) {
let strokeTemplate = dataItem.dataContext[strokeTemplateField];
if (strokeTemplate) {
if (!(strokeTemplate instanceof Template)) {
strokeTemplate = Template.new(strokeTemplate);
}
this._strokeTemplate = strokeTemplate;
if (i > dataItemIndex) {
currentEndIndex = i;
break;
}
else {
rangeStrokeTemplate = strokeTemplate;
stroke.template = strokeTemplate;
}
}
}
if (fillTemplateField) {
let fillTemplate = dataItem.dataContext[fillTemplateField];
if (fillTemplate) {
if (!(fillTemplate instanceof Template)) {
fillTemplate = Template.new(fillTemplate);
}
this._fillTemplate = fillTemplate;
if (i > dataItemIndex) {
currentEndIndex = i;
break;
}
else {
rangeFillTemplate = fillTemplate;
fill.template = fillTemplate;
}
}
}
if (!connect) {
let nextItem = dataItems[i + 1];
if (nextItem) {
if (baseAxis.shouldGap(dataItem, nextItem, autoGapCount, baseField)) {
points = [];
segments.push(points);
o.points = points;
}
}
}
}
fill.setRaw("userData", [dataItemIndex, i]);
stroke.setRaw("userData", [dataItemIndex, i]);
if (i === endIndex) {
this._endLine(points, segments[0][0]);
}
if (stroke) {
this._drawStroke(stroke, segments);
}
if (fill) {
this._drawFill(fill, segments);
}
this.axisRanges.each((axisRange) => {
const container = axisRange.container;
const fills = axisRange.fills;
const fill = this.makeFill(fills);
if (container) {
container.children.push(fill);
}
fill.setPrivate("visible", true);
this._drawFill(fill, segments);
const strokes = axisRange.strokes;
const stroke = this.makeStroke(strokes);
if (container) {
container.children.push(stroke);
}
if (rangeStrokeTemplate && rangeStrokeTemplate != this.strokes.template) {
stroke.template = rangeStrokeTemplate;
}
if (rangeFillTemplate && rangeFillTemplate != this.fills.template) {
fill.template = rangeFillTemplate;
}
stroke.setPrivate("visible", true);
this._drawStroke(stroke, segments);
fill.setRaw("userData", [dataItemIndex, i]);
stroke.setRaw("userData", [dataItemIndex, i]);
});
}
_getPoints(dataItem, o) {
let points = o.points;
let itemLocationX = dataItem.get("locationX", o.locationX);
let itemLocationY = dataItem.get("locationY", o.locationY);
const exactLocationX = this.get("exactLocationX", false);
const exactLocationY = this.get("exactLocationY", false);
let xPos = o.xAxis.getDataItemPositionX(dataItem, o.xField, itemLocationX, o.vcx, exactLocationX);
let yPos = o.yAxis.getDataItemPositionY(dataItem, o.yField, itemLocationY, o.vcy, exactLocationY);
if (this._shouldInclude(xPos)) {
const iPoint = this.getPoint(xPos, yPos);
const point = [iPoint.x, iPoint.y];
iPoint.x += this._x;
iPoint.y += this._y;
dataItem.set("point", iPoint);
if (o.fillVisible) {
let xPos0 = xPos;
let yPos0 = yPos;
if (o.baseAxis === o.xAxis) {
yPos0 = o.basePosY;
}
else if (o.baseAxis === o.yAxis) {
xPos0 = o.basePosX;
}
if (o.getOpen) {
let valueX = dataItem.get(o.xOpenField);
let valueY = dataItem.get(o.yOpenField);
if (valueX != null && valueY != null) {
let itemLocationX = dataItem.get("openLocationX", o.openLocationX);
let itemLocationY = dataItem.get("openLocationY", o.openLocationY);
if (o.stacked) {
let stackToItemX = dataItem.get("stackToItemX");
let stackToItemY = dataItem.get("stackToItemY");
if (stackToItemX) {
xPos0 = o.xAxis.getDataItemPositionX(stackToItemX, o.xField, itemLocationX, stackToItemX.component.get("vcx"), exactLocationX);
if ($type.isNaN(xPos0)) {
xPos0 = o.basePosX;
}
}
else {
if (o.yAxis === o.baseAxis) {
xPos0 = o.basePosX;
}
else {
xPos0 = o.xAxis.getDataItemPositionX(dataItem, o.xOpenField, itemLocationX, o.vcx, exactLocationX);
}
}
if (stackToItemY) {
yPos0 = o.yAxis.getDataItemPositionY(stackToItemY, o.yField, itemLocationY, stackToItemY.component.get("vcy"), exactLocationY);
if ($type.isNaN(yPos0)) {
yPos0 = o.basePosY;
}
}
else {
if (o.xAxis === o.baseAxis) {
yPos0 = o.basePosY;
}
else {
yPos0 = o.yAxis.getDataItemPositionY(dataItem, o.yOpenField, itemLocationY, o.vcy, exactLocationY);
}
}
}
else {
xPos0 = o.xAxis.getDataItemPositionX(dataItem, o.xOpenField, itemLocationX, o.vcx, exactLocationX);
yPos0 = o.yAxis.getDataItemPositionY(dataItem, o.yOpenField, itemLocationY, o.vcy, exactLocationY);
}
}
}
let closeIPoint = this.getPoint(xPos0, yPos0);
point[2] = closeIPoint.x;
point[3] = closeIPoint.y;
}
if (o.minDistance > 0) {
const p0 = point[0];
const p1 = point[1];
const p2 = point[2];
const p3 = point[3];
const prev = this._previousPoint;
const pp0 = prev[0];
const pp1 = prev[1];
const pp2 = prev[2];
const pp3 = prev[3];
if (Math.hypot(p0 - pp0, p1 - pp1) > o.minDistance || (p2 && p3 && Math.hypot(p2 - pp2, p3 - pp3) > o.minDistance)) {
points.push(point);
this._previousPoint = point;
}
}
else {
points.push(point);
}
}
}
_endLine(_points, _firstPoint) {
}
_drawStroke(graphics, segments) {
if (graphics.get("visible") && !graphics.get("forceHidden")) {
graphics.set("draw", (display) => {
$array.each(segments, (segment) => {
this._strokeGenerator.context(display);
this._strokeGenerator(segment);
});
});
}
}
_drawFill(graphics, segments) {
if (graphics.get("visible") && !graphics.get("forceHidden")) {
graphics.set("draw", (display) => {
$array.each(segments, (segment) => {
this._fillGenerator.context(display);
this._fillGenerator(segment);
});
});
}
}
_processAxisRange(axisRange) {
super._processAxisRange(axisRange);
axisRange.fills = new ListTemplate(Template.new({}), () => Graphics._new(this._root, {
themeTags: $utils.mergeTags(axisRange.fills.template.get("themeTags", []), ["line", "series", "fill"]),
}, [this.fills.template, axisRange.fills.template]));
axisRange.strokes = new ListTemplate(Template.new({}), () => Graphics._new(this._root, {
themeTags: $utils.mergeTags(axisRange.strokes.template.get("themeTags", []), ["line", "series", "stroke"]),
}, [this.strokes.template, axisRange.strokes.template]));
}
/**
* @ignore
*/
createLegendMarker(_dataItem) {
const legendDataItem = this.get("legendDataItem");
if (legendDataItem) {
const marker = legendDataItem.get("marker");
const markerRectangle = legendDataItem.get("markerRectangle");
if (markerRectangle) {
markerRectangle.setPrivate("visible", false);
}
marker.set("background", Rectangle.new(marker._root, { fillOpacity: 0, fill: color(0x000000) }));
const legendStroke = marker.children.push(Graphics._new(marker._root, {
themeTags: ["line", "series", "legend", "marker", "stroke"], interactive: false
}, [this.strokes.template]));
this._legendStroke = legendStroke;
const legendFill = marker.children.push(Graphics._new(marker._root, {
themeTags: ["line", "series", "legend", "marker", "fill"]
}, [this.fills.template]));
this._legendFill = legendFill;
const disabledColor = this._root.interfaceColors.get("disabled");
legendStroke.states._createC("disabled", { fill: disabledColor, stroke: disabledColor });
legendFill.states._createC("disabled", { fill: disabledColor, stroke: disabledColor });
if (this.bullets.length > 0) {
const bulletFunction = this.bullets.getIndex(0);
if (bulletFunction) {
const bullet = bulletFunction(marker._root, this, new DataItem(this, { legend: true }, {}));
if (bullet) {
const sprite = bullet.get("sprite");
if (sprite instanceof Graphics) {
sprite.states._createC("disabled", { fill: disabledColor, stroke: disabledColor });
}
if (sprite) {
// legend bullets are created straight from the bullet
// function, bypassing `_makeBulletReal`, so apply the same
// series-paint inheritance here — otherwise a bullet with
// no fill of its own is invisible in the legend
this._applyBulletDefaultPaint(sprite);
sprite.setAll({
tooltipText: undefined,
tooltipHTML: undefined,
focusable: undefined,
focusableGroup: undefined,
ariaLabel: undefined
});
marker.children.push(sprite);
sprite.setAll({ x: marker.width() / 2, y: marker.height() / 2 });
marker.events.on("boundschanged", () => {
sprite.setAll({ x: marker.width() / 2, y: marker.height() / 2 });
});
}
}
}
}
}
}
}
LineSeries.className = "LineSeries";
LineSeries.classNames = XYSeries.classNames.concat([LineSeries.className]);
//# sourceMappingURL=LineSeries.js.map