UNPKG

@progress/kendo-charts

Version:

Kendo UI platform-independent Charts library

263 lines (216 loc) 8.22 kB
/* eslint-disable camelcase */ import { drawing } from '@progress/kendo-drawing'; import { SankeyElement } from './element'; import { deepExtend } from '../common'; import { defined } from '../drawing-utils'; import { ARIA_ACTIVE_DESCENDANT } from '../common/constants'; var bezierPoint = function (p1, p2, p3, p4, t) { var t1 = 1 - t; var t1t1 = t1 * t1; var tt = t * t; return (p1 * t1t1 * t1) + (3 * p2 * t * t1t1) + (3 * p3 * tt * t1) + (p4 * tt * t); }; function calculatePerpendicularLine(x1, y1, x2, y2, L) { // 1. Calculate the midpoint M var xM = (x1 + x2) / 2; var yM = (y1 + y2) / 2; var dx, dy; if (y1 === y2) { // The line AB is horizontal dx = 0; dy = L / 2; } else if (x1 === x2) { // The line AB is vertical dx = L / 2; dy = 0; } else { // Common case when the line is not horizontal or vertical // 2. Calculate the slope of the original line var m = (y2 - y1) / (x2 - x1); // 3. Calculate the slope of the perpendicular line var mPerp = -1 / m; // 4. Calculate dx and dy dx = (L / 2) / Math.sqrt(1 + mPerp * mPerp); dy = mPerp * dx; } // 5. Coordinates of the points of the perpendicular line var P1 = { x: xM - dx, y: yM - dy }; var P2 = { x: xM + dx, y: yM + dy }; return { P1: P1, P2: P2 }; } function findIntersection(a, b, L, p, q) { // Midpoint between a and b var midpoint = { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 }; // Vector of the line ab var ab_dx = b.x - a.x; var ab_dy = b.y - a.y; // Vector, perpendicular to ab var perp_dx = -ab_dy; var perp_dy = ab_dx; // Normalize the perpendicular vector and scale it to 2*L var magnitude = Math.sqrt(perp_dx * perp_dx + perp_dy * perp_dy); perp_dx = (perp_dx / magnitude) * L; perp_dy = (perp_dy / magnitude) * L; // The endpoints of the perpendicular, 2*L long var c1 = { x: midpoint.x + perp_dx, y: midpoint.y + perp_dy }; var c2 = { x: midpoint.x - perp_dx, y: midpoint.y - perp_dy }; // Check for intersection of the lines pq and the perpendicular var pq_dx = q.x - p.x; var pq_dy = q.y - p.y; var denominator = (pq_dy) * (c1.x - c2.x) - (pq_dx) * (c1.y - c2.y); if (Math.abs(denominator) < 1e-10) { // The lines are almost parallel, no intersection return null; } var ua = (pq_dx * (c2.y - p.y) - pq_dy * (c2.x - p.x)) / denominator; var ub = ((c1.x - c2.x) * (c2.y - p.y) - (c1.y - c2.y) * (c2.x - p.x)) / denominator; if (ua >= 0 && ua <= 1 && ub >= 0 && ub <= 1) { var intersection = { x: c2.x + ua * (c1.x - c2.x), // y: c2.y + ua * (c1.y - c2.y) }; return intersection; } // No intersection of the segments return null; } var calculateControlPointsOffsetX = function (link, rtl) { var halfWidth = link.width / 2; var x0 = rtl ? link.x1 : link.x0; var x1 = rtl ? link.x0 : link.x1; var y0 = rtl ? link.y1 : link.y0; var y1 = rtl ? link.y0 : link.y1; var xC = (x0 + x1) / 2; var middlePoint = [xC, bezierPoint(y0, y0, y1, y1, 0.5)]; var tH = 0.4999; var pointH = [ bezierPoint(x0, xC, xC, x1, tH), bezierPoint(y0, y0, y1, y1, tH) ]; var line = calculatePerpendicularLine(middlePoint[0], middlePoint[1], pointH[0], pointH[1], link.width); var middlePointDown = [xC, bezierPoint(y0 + halfWidth, y0 + halfWidth, y1 + halfWidth, y1 + halfWidth, 0.5)]; // const middlePointUp = [xC, bezierPoint(y0 - halfWidth, y0 - halfWidth, y1 - halfWidth, y1 - halfWidth, 0.5)]; var P = line.P1.y > line.P2.y ? line.P1 : line.P2; var L = halfWidth; var LDir = (y0 > y1 ? 1 : -1) * L; var a = P; var b = { x: middlePointDown[0], y: middlePointDown[1] }; var p = { x: middlePointDown[0], y: middlePointDown[1] }; var q = { x: Math.max(1, middlePointDown[0] + LDir), y: middlePointDown[1] }; var Pmx = findIntersection(a, b, L, p, q) || { x: (middlePointDown[0] + P.x) / 2 }; var P1 = x0; var P4 = x1; var P2 = (Pmx.x - (0.125 * P1) - (0.125 * P4)) / 0.75; return xC - P2; }; export var Link = (function (SankeyElement) { function Link () { SankeyElement.apply(this, arguments); } if ( SankeyElement ) Link.__proto__ = SankeyElement; Link.prototype = Object.create( SankeyElement && SankeyElement.prototype ); Link.prototype.constructor = Link; Link.prototype.getElement = function getElement () { var link = this.options.link; var x0 = link.x0; var x1 = link.x1; var y0 = link.y0; var y1 = link.y1; var xC = (x0 + x1) / 2; return new drawing.Path(this.visualOptions()) .moveTo(x0, y0).curveTo([xC, y0], [xC, y1], [x1, y1]); }; Link.prototype.getLabelText = function getLabelText (options) { var labelTemplate = options.labels.ariaTemplate; if (labelTemplate) { return labelTemplate({ link: options.link }); } }; Link.prototype.visualOptions = function visualOptions () { var options = this.options; var link = this.options.link; var ariaLabel = this.getLabelText(options); return { stroke: { width: options.link.width, color: link.color || options.color, opacity: defined(link.opacity) ? link.opacity : options.opacity }, role: 'graphics-symbol', ariaRoleDescription: 'Link', ariaLabel: ariaLabel }; }; Link.prototype.createFocusHighlight = function createFocusHighlight () { if (!this.options.navigatable) { return; } var ref = this.options; var link = ref.link; var x0 = link.x0; var x1 = link.x1; var y0 = link.y0; var y1 = link.y1; var xC = (x0 + x1) / 2; var halfWidth = link.width / 2; var offset = calculateControlPointsOffsetX(link, this.options.rtl); this._highlight = new drawing.Path({ stroke: this.options.focusHighlight.border, visible: false }) .moveTo(x0, y0 + halfWidth) .lineTo(x0, y0 - halfWidth) .curveTo([xC + offset, y0 - halfWidth], [xC + offset, y1 - halfWidth], [x1, y1 - halfWidth]) .lineTo(x1, y1 + halfWidth) .curveTo([xC - offset, y1 + halfWidth], [xC - offset, y0 + halfWidth], [x0, y0 + halfWidth]); }; Link.prototype.focus = function focus (options) { if (this._highlight) { var ref = options || {}; var highlight = ref.highlight; if ( highlight === void 0 ) highlight = true; if (highlight) { this._highlight.options.set('visible', true); } var id = (this.options.link.sourceId) + "->" + (this.options.link.targetId); this.visual.options.set('id', id); if (this.options.root()) { this.options.root().setAttribute(ARIA_ACTIVE_DESCENDANT, id); } } }; Link.prototype.blur = function blur () { if (this._highlight) { this._highlight.options.set('visible', false); this.visual.options.set('id', ''); if (this.options.root()) { this.options.root().removeAttribute(ARIA_ACTIVE_DESCENDANT); } } }; return Link; }(SankeyElement)); export var resolveLinkOptions = function (link, options, sourceNode, targetNode) { var linkOptions = deepExtend({}, options, { link: link, opacity: link.opacity, color: link.color, colorType: link.colorType, visual: link.visual, highlight: link.highlight } ); if (linkOptions.colorType === 'source') { linkOptions.color = sourceNode.options.fill.color; } else if (linkOptions.colorType === 'target') { linkOptions.color = targetNode.options.fill.color; } return linkOptions; };