| let util = require("../../../../../util"); | |
| 
 | |
| /** | |
|  * The Base Class for all edges. | |
|  * | |
|  */ | |
| class EdgeBase { | |
|   /** | |
|    * @param {Object} options | |
|    * @param {Object} body | |
|    * @param {Label} labelModule | |
|    */ | |
|   constructor(options, body, labelModule) { | |
|     this.body = body; | |
|     this.labelModule = labelModule; | |
|     this.options = {}; | |
|     this.setOptions(options); | |
|     this.colorDirty = true; | |
|     this.color = {}; | |
|     this.selectionWidth = 2; | |
|     this.hoverWidth = 1.5; | |
|     this.fromPoint = this.from; | |
|     this.toPoint = this.to; | |
|   } | |
| 
 | |
|   /** | |
|    * Connects a node to itself | |
|    */ | |
|   connect() { | |
|     this.from = this.body.nodes[this.options.from]; | |
|     this.to = this.body.nodes[this.options.to]; | |
|   } | |
| 
 | |
|   /** | |
|    * | |
|    * @returns {boolean} always false | |
|    */ | |
|   cleanup() { | |
|     return false; | |
|   } | |
| 
 | |
|   /** | |
|    * | |
|    * @param {Object} options | |
|    */ | |
|   setOptions(options) { | |
|     this.options = options; | |
|     this.from = this.body.nodes[this.options.from]; | |
|     this.to = this.body.nodes[this.options.to]; | |
|     this.id = this.options.id; | |
|   } | |
| 
 | |
|   /** | |
|    * Redraw a edge as a line | |
|    * Draw this edge in the given canvas | |
|    * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); | |
|    * | |
|    * @param {CanvasRenderingContext2D}   ctx | |
|    * @param {Array} values | |
|    * @param {boolean} selected | |
|    * @param {boolean} hover | |
|    * @param {Node} viaNode | |
|    * @private | |
|    */ | |
|   drawLine(ctx, values, selected, hover, viaNode) { | |
|     // set style | |
|     ctx.strokeStyle = this.getColor(ctx, values, selected, hover); | |
|     ctx.lineWidth = values.width; | |
| 
 | |
|     if (values.dashes !== false) { | |
|       this._drawDashedLine(ctx, values, viaNode); | |
|     } | |
|     else { | |
|       this._drawLine(ctx, values, viaNode); | |
|     } | |
|   } | |
| 
 | |
| 
 | |
|   /** | |
|    * | |
|    * @param {CanvasRenderingContext2D}   ctx | |
|    * @param {Array} values | |
|    * @param {Node} viaNode | |
|    * @param {{x: number, y: number}} [fromPoint] | |
|    * @param {{x: number, y: number}} [toPoint] | |
|    * @private | |
|    */ | |
|   _drawLine(ctx, values, viaNode, fromPoint, toPoint) { | |
|     if (this.from != this.to) { | |
|       // draw line | |
|       this._line(ctx, values, viaNode, fromPoint, toPoint); | |
|     } | |
|     else { | |
|       let [x,y,radius] = this._getCircleData(ctx); | |
|       this._circle(ctx, values, x, y, radius); | |
|     } | |
|   } | |
| 
 | |
|   /** | |
|    * | |
|    * @param {CanvasRenderingContext2D} ctx | |
|    * @param {Array} values | |
|    * @param {Node} viaNode | |
|    * @param {{x: number, y: number}} [fromPoint]  TODO: Remove in next major release | |
|    * @param {{x: number, y: number}} [toPoint]    TODO: Remove in next major release | |
|    * @private | |
|    */ | |
|   _drawDashedLine(ctx, values, viaNode, fromPoint, toPoint) {  // eslint-disable-line no-unused-vars | |
|     ctx.lineCap = 'round'; | |
|     let pattern = [5,5]; | |
|     if (Array.isArray(values.dashes) === true) { | |
|       pattern = values.dashes; | |
|     } | |
| 
 | |
|     // only firefox and chrome support this method, else we use the legacy one. | |
|     if (ctx.setLineDash !== undefined) { | |
|       ctx.save(); | |
| 
 | |
|       // set dash settings for chrome or firefox | |
|       ctx.setLineDash(pattern); | |
|       ctx.lineDashOffset = 0; | |
| 
 | |
|       // draw the line | |
|       if (this.from != this.to) { | |
|         // draw line | |
|         this._line(ctx, values, viaNode); | |
|       } | |
|       else { | |
|         let [x,y,radius] = this._getCircleData(ctx); | |
|         this._circle(ctx, values, x, y, radius); | |
|       } | |
| 
 | |
|       // restore the dash settings. | |
|       ctx.setLineDash([0]); | |
|       ctx.lineDashOffset = 0; | |
|       ctx.restore(); | |
|     } | |
|     else { // unsupporting smooth lines | |
|       if (this.from != this.to) { | |
|         // draw line | |
|         ctx.dashedLine(this.from.x, this.from.y, this.to.x, this.to.y, pattern); | |
|       } | |
|       else { | |
|         let [x,y,radius] = this._getCircleData(ctx); | |
|         this._circle(ctx, values, x, y, radius); | |
|       } | |
|       // draw shadow if enabled | |
|       this.enableShadow(ctx, values); | |
| 
 | |
|       ctx.stroke(); | |
| 
 | |
|       // disable shadows for other elements. | |
|       this.disableShadow(ctx, values); | |
|     } | |
|   } | |
| 
 | |
| 
 | |
|   /** | |
|    * | |
|    * @param {Node} nearNode | |
|    * @param {CanvasRenderingContext2D} ctx | |
|    * @param {Object} options | |
|    * @returns {{x: number, y: number}} | |
|    */ | |
|   findBorderPosition(nearNode, ctx, options) { | |
|     if (this.from != this.to) { | |
|       return this._findBorderPosition(nearNode, ctx, options); | |
|     } | |
|     else { | |
|       return this._findBorderPositionCircle(nearNode, ctx, options); | |
|     } | |
|   } | |
| 
 | |
|   /** | |
|    * | |
|    * @param {CanvasRenderingContext2D} ctx | |
|    * @returns {{from: ({x: number, y: number, t: number}|*), to: ({x: number, y: number, t: number}|*)}} | |
|    */ | |
|   findBorderPositions(ctx) { | |
|     let from = {}; | |
|     let to = {}; | |
|     if (this.from != this.to) { | |
|       from = this._findBorderPosition(this.from, ctx); | |
|       to = this._findBorderPosition(this.to, ctx); | |
|     } | |
|     else { | |
|       let [x,y] = this._getCircleData(ctx).slice(0, 2); | |
| 
 | |
|       from = this._findBorderPositionCircle(this.from, ctx, {x, y, low:0.25, high:0.6, direction:-1}); | |
|       to = this._findBorderPositionCircle(this.from, ctx, {x, y, low:0.6, high:0.8, direction:1}); | |
|     } | |
|     return {from, to}; | |
|   } | |
| 
 | |
|   /** | |
|    * | |
|    * @param {CanvasRenderingContext2D} ctx | |
|    * @returns {Array.<number>} x, y, radius | |
|    * @private | |
|    */ | |
|   _getCircleData(ctx) { | |
|     let x, y; | |
|     let node = this.from; | |
|     let radius = this.options.selfReferenceSize; | |
| 
 | |
|     if (ctx !== undefined) { | |
|       if (node.shape.width === undefined) { | |
|         node.shape.resize(ctx); | |
|       } | |
|     } | |
| 
 | |
|     // get circle coordinates | |
|     if (node.shape.width > node.shape.height) { | |
|       x = node.x + node.shape.width * 0.5; | |
|       y = node.y - radius; | |
|     } | |
|     else { | |
|       x = node.x + radius; | |
|       y = node.y - node.shape.height * 0.5; | |
|     } | |
|     return [x,y,radius]; | |
|   } | |
| 
 | |
|   /** | |
|    * Get a point on a circle | |
|    * @param {number} x | |
|    * @param {number} y | |
|    * @param {number} radius | |
|    * @param {number} percentage - Value between 0 (line start) and 1 (line end) | |
|    * @return {Object} point | |
|    * @private | |
|    */ | |
|   _pointOnCircle(x, y, radius, percentage) { | |
|     let angle = percentage * 2 * Math.PI; | |
|     return { | |
|       x: x + radius * Math.cos(angle), | |
|       y: y - radius * Math.sin(angle) | |
|     } | |
|   } | |
| 
 | |
|   /** | |
|    * This function uses binary search to look for the point where the circle crosses the border of the node. | |
|    * @param {Node} node | |
|    * @param {CanvasRenderingContext2D} ctx | |
|    * @param {Object} options | |
|    * @returns {*} | |
|    * @private | |
|    */ | |
|   _findBorderPositionCircle(node, ctx, options) { | |
|     let x = options.x; | |
|     let y = options.y; | |
|     let low = options.low; | |
|     let high = options.high; | |
|     let direction = options.direction; | |
| 
 | |
|     let maxIterations = 10; | |
|     let iteration = 0; | |
|     let radius = this.options.selfReferenceSize; | |
|     let pos, angle, distanceToBorder, distanceToPoint, difference; | |
|     let threshold = 0.05; | |
|     let middle = (low + high) * 0.5; | |
| 
 | |
|     while (low <= high && iteration < maxIterations) { | |
|       middle = (low + high) * 0.5; | |
| 
 | |
|       pos = this._pointOnCircle(x, y, radius, middle); | |
|       angle = Math.atan2((node.y - pos.y), (node.x - pos.x)); | |
|       distanceToBorder = node.distanceToBorder(ctx, angle); | |
|       distanceToPoint = Math.sqrt(Math.pow(pos.x - node.x, 2) + Math.pow(pos.y - node.y, 2)); | |
|       difference = distanceToBorder - distanceToPoint; | |
|       if (Math.abs(difference) < threshold) { | |
|         break; // found | |
|       } | |
|       else if (difference > 0) { // distance to nodes is larger than distance to border --> t needs to be bigger if we're looking at the to node. | |
|         if (direction > 0) { | |
|           low = middle; | |
|         } | |
|         else { | |
|           high = middle; | |
|         } | |
|       } | |
|       else { | |
|         if (direction > 0) { | |
|           high = middle; | |
|         } | |
|         else { | |
|           low = middle; | |
|         } | |
|       } | |
|       iteration++; | |
| 
 | |
|     } | |
|     pos.t = middle; | |
| 
 | |
|     return pos; | |
|   } | |
| 
 | |
|   /** | |
|    * Get the line width of the edge. Depends on width and whether one of the | |
|    * connected nodes is selected. | |
|    * @param {boolean} selected | |
|    * @param {boolean} hover | |
|    * @returns {number} width | |
|    * @private | |
|    */ | |
|   getLineWidth(selected, hover) { | |
|     if (selected === true) { | |
|       return Math.max(this.selectionWidth, 0.3 / this.body.view.scale); | |
|     } | |
|     else { | |
|       if (hover === true) { | |
|         return Math.max(this.hoverWidth, 0.3 / this.body.view.scale); | |
|       } | |
|       else { | |
|         return Math.max(this.options.width, 0.3 / this.body.view.scale); | |
|       } | |
|     } | |
|   } | |
| 
 | |
|   /** | |
|    * | |
|    * @param {CanvasRenderingContext2D} ctx | |
|    * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values | |
|    * @param {boolean} selected - Unused | |
|    * @param {boolean} hover - Unused | |
|    * @returns {string} | |
|    */ | |
|   getColor(ctx, values, selected, hover) {  // eslint-disable-line no-unused-vars | |
|     if (values.inheritsColor !== false) { | |
|       // when this is a loop edge, just use the 'from' method | |
|       if ((values.inheritsColor === 'both') && (this.from.id !== this.to.id)) { | |
|         let grd = ctx.createLinearGradient(this.from.x, this.from.y, this.to.x, this.to.y); | |
|         let fromColor, toColor; | |
|         fromColor = this.from.options.color.highlight.border; | |
|         toColor = this.to.options.color.highlight.border; | |
| 
 | |
|         if ((this.from.selected === false) && (this.to.selected === false)) { | |
|           fromColor = util.overrideOpacity(this.from.options.color.border, values.opacity); | |
|           toColor = util.overrideOpacity(this.to.options.color.border, values.opacity); | |
|         } | |
|         else if ((this.from.selected === true) && (this.to.selected === false)) { | |
|           toColor = this.to.options.color.border; | |
|         } | |
|         else if ((this.from.selected === false) && (this.to.selected === true)) { | |
|           fromColor = this.from.options.color.border; | |
|         } | |
|         grd.addColorStop(0, fromColor); | |
|         grd.addColorStop(1, toColor); | |
| 
 | |
|         // -------------------- this returns -------------------- // | |
|         return grd; | |
|       } | |
| 
 | |
|       if (values.inheritsColor === "to") { | |
|         return util.overrideOpacity(this.to.options.color.border, values.opacity); | |
|       } else { // "from" | |
|         return util.overrideOpacity(this.from.options.color.border, values.opacity); | |
|       } | |
|     } else { | |
|       return util.overrideOpacity(values.color, values.opacity); | |
|     } | |
|   } | |
| 
 | |
|   /** | |
|    * Draw a line from a node to itself, a circle | |
|    * | |
|    * @param {CanvasRenderingContext2D} ctx | |
|    * @param {Array} values | |
|    * @param {number} x | |
|    * @param {number} y | |
|    * @param {number} radius | |
|    * @private | |
|    */ | |
|   _circle(ctx, values, x, y, radius) { | |
|     // draw shadow if enabled | |
|     this.enableShadow(ctx, values); | |
| 
 | |
|     // draw a circle | |
|     ctx.beginPath(); | |
|     ctx.arc(x, y, radius, 0, 2 * Math.PI, false); | |
|     ctx.stroke(); | |
| 
 | |
|     // disable shadows for other elements. | |
|     this.disableShadow(ctx, values); | |
|   } | |
| 
 | |
| 
 | |
|   /** | |
|    * Calculate the distance between a point (x3,y3) and a line segment from | |
|    * (x1,y1) to (x2,y2). | |
|    * x3,y3 is the point. | |
|    * http://stackoverflow.com/questions/849211/shortest-distancae-between-a-point-and-a-line-segment | |
|    * @param {number} x1 | |
|    * @param {number} y1 | |
|    * @param {number} x2 | |
|    * @param {number} y2 | |
|    * @param {number} x3 | |
|    * @param {number} y3 | |
|    * @param {Node} via | |
|    * @param {Array} values | |
|    * @returns {number} | |
|    * @private | |
|    */ | |
|   getDistanceToEdge(x1, y1, x2, y2, x3, y3, via, values) {  // eslint-disable-line no-unused-vars | |
|     let returnValue = 0; | |
|     if (this.from != this.to) { | |
|       returnValue = this._getDistanceToEdge(x1, y1, x2, y2, x3, y3, via) | |
|     } | |
|     else { | |
|       let [x,y,radius] = this._getCircleData(undefined); | |
|       let dx = x - x3; | |
|       let dy = y - y3; | |
|       returnValue = Math.abs(Math.sqrt(dx * dx + dy * dy) - radius); | |
|     } | |
| 
 | |
|     if (this.labelModule.size.left < x3 && | |
|       this.labelModule.size.left + this.labelModule.size.width > x3 && | |
|       this.labelModule.size.top < y3 && | |
|       this.labelModule.size.top + this.labelModule.size.height > y3) { | |
|       return 0; | |
|     } | |
|     else { | |
|       return returnValue; | |
|     } | |
|   } | |
| 
 | |
|   /** | |
|    * | |
|    * @param {number} x1 | |
|    * @param {number} y1 | |
|    * @param {number} x2 | |
|    * @param {number} y2 | |
|    * @param {number} x3 | |
|    * @param {number} y3 | |
|    * @returns {number} | |
|    * @private | |
|    */ | |
|   _getDistanceToLine(x1, y1, x2, y2, x3, y3) { | |
|     let px = x2 - x1; | |
|     let py = y2 - y1; | |
|     let something = px * px + py * py; | |
|     let u = ((x3 - x1) * px + (y3 - y1) * py) / something; | |
| 
 | |
|     if (u > 1) { | |
|       u = 1; | |
|     } | |
|     else if (u < 0) { | |
|       u = 0; | |
|     } | |
| 
 | |
|     let x = x1 + u * px; | |
|     let y = y1 + u * py; | |
|     let dx = x - x3; | |
|     let dy = y - y3; | |
| 
 | |
|     //# Note: If the actual distance does not matter, | |
|     //# if you only want to compare what this function | |
|     //# returns to other results of this function, you | |
|     //# can just return the squared distance instead | |
|     //# (i.e. remove the sqrt) to gain a little performance | |
|  | |
|     return Math.sqrt(dx * dx + dy * dy); | |
|   } | |
| 
 | |
| 
 | |
|   /** | |
|    * @param {CanvasRenderingContext2D} ctx | |
|    * @param {string} position | |
|    * @param {Node} viaNode | |
|    * @param {boolean} selected | |
|    * @param {boolean} hover | |
|    * @param {Array} values | |
|    * @returns {{point: *, core: {x: number, y: number}, angle: *, length: number, type: *}} | |
|    */ | |
|   getArrowData(ctx, position, viaNode, selected, hover, values) { | |
|     // set lets | |
|     let angle; | |
|     let arrowPoint; | |
|     let node1; | |
|     let node2; | |
|     let guideOffset; | |
|     let scaleFactor; | |
|     let type; | |
|     let lineWidth = values.width; | |
| 
 | |
|     if (position === 'from') { | |
|       node1 = this.from; | |
|       node2 = this.to; | |
|       guideOffset = 0.1; | |
|       scaleFactor = values.fromArrowScale; | |
|       type = values.fromArrowType; | |
|     } | |
|     else if (position === 'to') { | |
|       node1 = this.to; | |
|       node2 = this.from; | |
|       guideOffset = -0.1; | |
|       scaleFactor = values.toArrowScale; | |
|       type = values.toArrowType; | |
|     } | |
|     else { | |
|       node1 = this.to; | |
|       node2 = this.from; | |
|       scaleFactor = values.middleArrowScale; | |
|       type = values.middleArrowType; | |
|     } | |
| 
 | |
|     // if not connected to itself | |
|     if (node1 != node2) { | |
|       if (position !== 'middle') { | |
|         // draw arrow head | |
|         if (this.options.smooth.enabled === true) { | |
|           arrowPoint = this.findBorderPosition(node1, ctx, { via: viaNode }); | |
|           let guidePos = this.getPoint(Math.max(0.0, Math.min(1.0, arrowPoint.t + guideOffset)), viaNode); | |
|           angle = Math.atan2((arrowPoint.y - guidePos.y), (arrowPoint.x - guidePos.x)); | |
|         } else { | |
|           angle = Math.atan2((node1.y - node2.y), (node1.x - node2.x)); | |
|           arrowPoint = this.findBorderPosition(node1, ctx); | |
|         } | |
|       } else { | |
|         angle = Math.atan2((node1.y - node2.y), (node1.x - node2.x)); | |
|         arrowPoint = this.getPoint(0.5, viaNode); // this is 0.6 to account for the size of the arrow. | |
|       } | |
|     } else { | |
|       // draw circle | |
|       let [x,y,radius] = this._getCircleData(ctx); | |
| 
 | |
|       if (position === 'from') { | |
|         arrowPoint = this.findBorderPosition(this.from, ctx, { x, y, low: 0.25, high: 0.6, direction: -1 }); | |
|         angle = arrowPoint.t * -2 * Math.PI + 1.5 * Math.PI + 0.1 * Math.PI; | |
|       } else if (position === 'to') { | |
|         arrowPoint = this.findBorderPosition(this.from, ctx, { x, y, low: 0.6, high: 1.0, direction: 1 }); | |
|         angle = arrowPoint.t * -2 * Math.PI + 1.5 * Math.PI - 1.1 * Math.PI; | |
|       } else { | |
|         arrowPoint = this._pointOnCircle(x, y, radius, 0.175); | |
|         angle = 3.9269908169872414; // === 0.175 * -2 * Math.PI + 1.5 * Math.PI + 0.1 * Math.PI; | |
|       } | |
|     } | |
| 
 | |
|     let length = 15 * scaleFactor + 3 * lineWidth; // 3* lineWidth is the width of the edge. | |
|  | |
|     var xi = arrowPoint.x - length * 0.9 * Math.cos(angle); | |
|     var yi = arrowPoint.y - length * 0.9 * Math.sin(angle); | |
|     let arrowCore = { x: xi, y: yi }; | |
| 
 | |
|     return { point: arrowPoint, core: arrowCore, angle: angle, length: length, type: type }; | |
|   } | |
| 
 | |
|   /** | |
|    * | |
|    * @param {CanvasRenderingContext2D} ctx | |
|    * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values | |
|    * @param {boolean} selected | |
|    * @param {boolean} hover | |
|    * @param {Object} arrowData | |
|    */ | |
|   drawArrowHead(ctx, values, selected, hover, arrowData) { | |
|     // set style | |
|     ctx.strokeStyle = this.getColor(ctx, values, selected, hover); | |
|     ctx.fillStyle = ctx.strokeStyle; | |
|     ctx.lineWidth = values.width; | |
| 
 | |
|     if (arrowData.type && arrowData.type.toLowerCase() === 'circle') { | |
|       // draw circle at the end of the line | |
|       ctx.circleEndpoint(arrowData.point.x, arrowData.point.y, arrowData.angle, arrowData.length); | |
|     } else { | |
|       // draw arrow at the end of the line | |
|       ctx.arrowEndpoint(arrowData.point.x, arrowData.point.y, arrowData.angle, arrowData.length); | |
|     } | |
| 
 | |
|     // draw shadow if enabled | |
|     this.enableShadow(ctx, values); | |
|     ctx.fill(); | |
|     // disable shadows for other elements. | |
|     this.disableShadow(ctx, values); | |
|   } | |
| 
 | |
| 
 | |
|   /** | |
|    * | |
|    * @param {CanvasRenderingContext2D} ctx | |
|    * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values | |
|    */ | |
|   enableShadow(ctx, values) { | |
|     if (values.shadow === true) { | |
|       ctx.shadowColor = values.shadowColor; | |
|       ctx.shadowBlur = values.shadowSize; | |
|       ctx.shadowOffsetX = values.shadowX; | |
|       ctx.shadowOffsetY = values.shadowY; | |
|     } | |
|   } | |
| 
 | |
|   /** | |
|    * | |
|    * @param {CanvasRenderingContext2D} ctx | |
|    * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values | |
|    */ | |
|   disableShadow(ctx, values) { | |
|     if (values.shadow === true) { | |
|       ctx.shadowColor = 'rgba(0,0,0,0)'; | |
|       ctx.shadowBlur = 0; | |
|       ctx.shadowOffsetX = 0; | |
|       ctx.shadowOffsetY = 0; | |
|     } | |
|   } | |
| } | |
| 
 | |
| export default EdgeBase;
 |