UNPKG

devextreme

Version:

HTML5 JavaScript Component Suite for Responsive Web Development

411 lines (351 loc) • 15.3 kB
"use strict"; // there are line, stepline, stackedline, fullstackedline, spline var series = require("./scatter_series"), chartScatterSeries = series.chart, polarScatterSeries = series.polar, objectUtils = require("../../core/utils/object"), extend = require("../../core/utils/extend").extend, each = require("../../core/utils/iterator").each, vizUtils = require("../core/utils"), normalizeAngle = vizUtils.normalizeAngle, DISCRETE = "discrete", _map = vizUtils.map, _extend = extend, _each = each; exports.chart = {}; exports.polar = {}; function clonePoint(point, newX, newY, newAngle) { var p = objectUtils.clone(point); p.x = newX; p.y = newY; p.angle = newAngle; return p; } function getTangentPoint(point, prevPoint, centerPoint, tan, nextStepAngle) { var correctAngle = point.angle + nextStepAngle, cosSin = vizUtils.getCosAndSin(correctAngle), x = centerPoint.x + (point.radius + tan * nextStepAngle) * cosSin.cos, y = centerPoint.y - (point.radius + tan * nextStepAngle) * cosSin.sin; return clonePoint(prevPoint, x, y, correctAngle); } var lineMethods = { _applyGroupSettings: function _applyGroupSettings(style, settings, group) { var that = this; settings = _extend(settings, style); that._applyElementsClipRect(settings); group.attr(settings); }, _setGroupsSettings: function _setGroupsSettings(animationEnabled) { var that = this, style = that._styles.normal; that._applyGroupSettings(style.elements, { "class": "dxc-elements" }, that._elementsGroup); that._bordersGroup && that._applyGroupSettings(style.border, { "class": "dxc-borders" }, that._bordersGroup); chartScatterSeries._setGroupsSettings.call(that, animationEnabled); animationEnabled && that._markersGroup && that._markersGroup.attr({ opacity: 0.001 }); }, _createGroups: function _createGroups() { var that = this; that._createGroup("_elementsGroup", that, that._group); that._areBordersVisible() && that._createGroup("_bordersGroup", that, that._group); chartScatterSeries._createGroups.call(that); }, _areBordersVisible: function _areBordersVisible() { return false; }, _getDefaultSegment: function _getDefaultSegment(segment) { return { line: _map(segment.line || [], function (pt) { return pt.getDefaultCoords(); }) }; }, _prepareSegment: function _prepareSegment(points) { return { line: points }; }, _parseLineOptions: function _parseLineOptions(options, defaultColor) { return { stroke: options.color || defaultColor, "stroke-width": options.width, dashStyle: options.dashStyle || 'solid' }; }, _parseStyle: function _parseStyle(options, defaultColor) { return { elements: this._parseLineOptions(options, defaultColor) }; }, _applyStyle: function _applyStyle(style) { var that = this; that._elementsGroup && that._elementsGroup.attr(style.elements); _each(that._graphics || [], function (_, graphic) { graphic.line && graphic.line.attr({ 'stroke-width': style.elements["stroke-width"] }).sharp(); }); }, _drawElement: function _drawElement(segment, group) { return { line: this._createMainElement(segment.line, { "stroke-width": this._styles.normal.elements["stroke-width"] }).append(group) }; }, _removeElement: function _removeElement(element) { element.line.remove(); }, _updateElement: function _updateElement(element, segment, animate, animationComplete) { var params = { points: segment.line }, lineElement = element.line; animate ? lineElement.animate(params, {}, animationComplete) : lineElement.attr(params); }, _animateComplete: function _animateComplete() { var that = this; chartScatterSeries._animateComplete.call(that); that._markersGroup && that._markersGroup.animate({ opacity: 1 }, { duration: that._defaultDuration }); }, _animate: function _animate() { var that = this, lastIndex = that._graphics.length - 1; _each(that._graphics || [], function (i, elem) { var complete; if (i === lastIndex) { complete = function complete() { that._animateComplete(); }; } that._updateElement(elem, that._segments[i], true, complete); }); }, _drawPoint: function _drawPoint(options) { chartScatterSeries._drawPoint.call(this, { point: options.point, groups: options.groups }); }, _createMainElement: function _createMainElement(points, settings) { return this._renderer.path(points, "line").attr(settings).sharp(); }, _sortPoints: function _sortPoints(points, rotated) { return rotated ? points.sort(function (p1, p2) { return p2.y - p1.y; }) : points.sort(function (p1, p2) { return p1.x - p2.x; }); }, _drawSegment: function _drawSegment(points, animationEnabled, segmentCount, lastSegment) { var that = this, rotated = that._options.rotated, forceDefaultSegment = false, segment = that._prepareSegment(points, rotated, lastSegment); that._segments.push(segment); if (!that._graphics[segmentCount]) { that._graphics[segmentCount] = that._drawElement(animationEnabled ? that._getDefaultSegment(segment) : segment, that._elementsGroup); } else if (!animationEnabled) { that._updateElement(that._graphics[segmentCount], segment); } else if (forceDefaultSegment) { that._updateElement(that._graphics[segmentCount], that._getDefaultSegment(segment)); } }, _getTrackerSettings: function _getTrackerSettings() { var that = this, defaultTrackerWidth = that._defaultTrackerWidth, strokeWidthFromElements = that._styles.normal.elements["stroke-width"]; return { "stroke-width": strokeWidthFromElements > defaultTrackerWidth ? strokeWidthFromElements : defaultTrackerWidth, fill: "none" }; }, _getMainPointsFromSegment: function _getMainPointsFromSegment(segment) { return segment.line; }, _drawTrackerElement: function _drawTrackerElement(segment) { return this._createMainElement(this._getMainPointsFromSegment(segment), this._getTrackerSettings(segment)); }, _updateTrackerElement: function _updateTrackerElement(segment, element) { var settings = this._getTrackerSettings(segment); settings.points = this._getMainPointsFromSegment(segment); element.attr(settings); } }; var lineSeries = exports.chart["line"] = _extend({}, chartScatterSeries, lineMethods); exports.chart["stepline"] = _extend({}, lineSeries, { _calculateStepLinePoints: function _calculateStepLinePoints(points) { var segment = []; _each(points, function (i, pt) { var stepY, point; if (!i) { segment.push(pt); return; } stepY = segment[segment.length - 1].y; if (stepY !== pt.y) { point = objectUtils.clone(pt); point.y = stepY; segment.push(point); } segment.push(pt); }); return segment; }, _prepareSegment: function _prepareSegment(points) { return lineSeries._prepareSegment(this._calculateStepLinePoints(points)); } }); exports.chart["spline"] = _extend({}, lineSeries, { _calculateBezierPoints: function _calculateBezierPoints(src, rotated) { var bezierPoints = [], pointsCopy = src, checkExtremum = function checkExtremum(otherPointCoord, pointCoord, controlCoord) { return otherPointCoord > pointCoord && controlCoord > otherPointCoord || otherPointCoord < pointCoord && controlCoord < otherPointCoord ? otherPointCoord : controlCoord; }; if (pointsCopy.length !== 1) { pointsCopy.forEach(function (curPoint, i) { var leftControlX, leftControlY, rightControlX, rightControlY, prevPoint = pointsCopy[i - 1], nextPoint = pointsCopy[i + 1], xCur, yCur, x1, x2, y1, y2, lambda = 0.5, curIsExtremum, leftPoint, rightPoint, a, b, c, xc, yc, shift; if (!i || i === pointsCopy.length - 1) { bezierPoints.push(curPoint, curPoint); return; } xCur = curPoint.x; yCur = curPoint.y; x1 = prevPoint.x; x2 = nextPoint.x; y1 = prevPoint.y; y2 = nextPoint.y; // check for extremum curIsExtremum = !!(!rotated && (yCur <= prevPoint.y && yCur <= nextPoint.y || yCur >= prevPoint.y && yCur >= nextPoint.y) || rotated && (xCur <= prevPoint.x && xCur <= nextPoint.x || xCur >= prevPoint.x && xCur >= nextPoint.x)); if (curIsExtremum) { if (!rotated) { rightControlY = leftControlY = yCur; rightControlX = (xCur + nextPoint.x) / 2; leftControlX = (xCur + prevPoint.x) / 2; } else { rightControlX = leftControlX = xCur; rightControlY = (yCur + nextPoint.y) / 2; leftControlY = (yCur + prevPoint.y) / 2; } } else { a = y2 - y1; b = x1 - x2; c = y1 * x2 - x1 * y2; if (!rotated) { if (!b) { bezierPoints.push(curPoint, curPoint, curPoint); return; } xc = xCur; yc = -1 * (a * xc + c) / b; shift = yc - yCur; y1 -= shift; y2 -= shift; } else { if (!a) { bezierPoints.push(curPoint, curPoint, curPoint); return; } yc = yCur; xc = -1 * (b * yc + c) / a; shift = xc - xCur; x1 -= shift; x2 -= shift; } rightControlX = (xCur + lambda * x2) / (1 + lambda); rightControlY = (yCur + lambda * y2) / (1 + lambda); leftControlX = (xCur + lambda * x1) / (1 + lambda); leftControlY = (yCur + lambda * y1) / (1 + lambda); } // check control points for extremum if (!rotated) { leftControlY = checkExtremum(prevPoint.y, yCur, leftControlY); rightControlY = checkExtremum(nextPoint.y, yCur, rightControlY); } else { leftControlX = checkExtremum(prevPoint.x, xCur, leftControlX); rightControlX = checkExtremum(nextPoint.x, xCur, rightControlX); } leftPoint = clonePoint(curPoint, leftControlX, leftControlY); rightPoint = clonePoint(curPoint, rightControlX, rightControlY); bezierPoints.push(leftPoint, curPoint, rightPoint); }); } else { bezierPoints.push(pointsCopy[0]); } return bezierPoints; }, _prepareSegment: function _prepareSegment(points, rotated) { return lineSeries._prepareSegment(this._calculateBezierPoints(points, rotated)); }, _createMainElement: function _createMainElement(points, settings) { return this._renderer.path(points, "bezier").attr(settings).sharp(); } }); exports.polar.line = _extend({}, polarScatterSeries, lineMethods, { _sortPoints: function _sortPoints(points) { return points; }, _prepareSegment: function _prepareSegment(points, rotated, lastSegment) { var preparedPoints = [], centerPoint = this.getValueAxis().getCenter(), i; lastSegment && this._closeSegment(points); if (this.argumentAxisType !== DISCRETE && this.valueAxisType !== DISCRETE) { for (i = 1; i < points.length; i++) { preparedPoints = preparedPoints.concat(this._getTangentPoints(points[i], points[i - 1], centerPoint)); } if (!preparedPoints.length) { // T174220 preparedPoints = points; } } else { return lineSeries._prepareSegment.call(this, points); } return { line: preparedPoints }; }, _getRemainingAngle: function _getRemainingAngle(angle) { var normAngle = normalizeAngle(angle); return angle >= 0 ? 360 - normAngle : -normAngle; }, _closeSegment: function _closeSegment(points) { var point, differenceAngle; if (this._segments.length) { point = this._segments[0].line[0]; } else { point = clonePoint(points[0], points[0].x, points[0].y, points[0].angle); } if (points[points.length - 1].angle !== point.angle) { if (normalizeAngle(Math.round(points[points.length - 1].angle)) === normalizeAngle(Math.round(point.angle))) { point.angle = points[points.length - 1].angle; } else { differenceAngle = points[points.length - 1].angle - point.angle; point.angle = points[points.length - 1].angle + this._getRemainingAngle(differenceAngle); } points.push(point); } }, _getTangentPoints: function _getTangentPoints(point, prevPoint, centerPoint) { var tangentPoints = [], betweenAngle = Math.round(prevPoint.angle - point.angle), tan = (prevPoint.radius - point.radius) / betweenAngle, i; if (betweenAngle === 0) { tangentPoints = [prevPoint, point]; } else if (betweenAngle > 0) { for (i = betweenAngle; i >= 0; i--) { tangentPoints.push(getTangentPoint(point, prevPoint, centerPoint, tan, i)); } } else { for (i = 0; i >= betweenAngle; i--) { tangentPoints.push(getTangentPoint(point, prevPoint, centerPoint, tan, betweenAngle - i)); } } return tangentPoints; } });