;(function() { 'use strict'; sigma.utils.pkg('sigma.canvas.edges'); /** * This edge renderer will display edges as curves with arrow heading. * * @param {object} edge The edge object. * @param {object} source node The edge source node. * @param {object} target node The edge target node. * @param {CanvasRenderingContext2D} context The canvas context. * @param {configurable} settings The settings function. */ sigma.canvas.edges.dotCurvedArrow = function(edge, source, target, context, settings) { var color = edge.color, prefix = settings('prefix') || '', edgeColor = settings('edgeColor'), defaultNodeColor = settings('defaultNodeColor'), defaultEdgeColor = settings('defaultEdgeColor'), cp = {}, size = edge[prefix + 'size'] || 1, count = edge.count || 0, tSize = target[prefix + 'size'], sX = source[prefix + 'x'], sY = source[prefix + 'y'], tX = target[prefix + 'x'], tY = target[prefix + 'y'], aSize = Math.max(size * 2.5, settings('minArrowSize')), d, aX, aY, vX, vY; cp = (source.id === target.id) ? sigma.utils.getSelfLoopControlPoints(sX, sY, tSize, count) : sigma.utils.getQuadraticControlPoint(sX, sY, tX, tY, count); if (source.id === target.id) { d = Math.sqrt(Math.pow(tX - cp.x1, 2) + Math.pow(tY - cp.y1, 2)); aX = cp.x1 + (tX - cp.x1) * (d - aSize - tSize) / d; aY = cp.y1 + (tY - cp.y1) * (d - aSize - tSize) / d; vX = (tX - cp.x1) * aSize / d; vY = (tY - cp.y1) * aSize / d; } else { d = Math.sqrt(Math.pow(tX - cp.x, 2) + Math.pow(tY - cp.y, 2)); aX = cp.x + (tX - cp.x) * (d - aSize - tSize) / d; aY = cp.y + (tY - cp.y) * (d - aSize - tSize) / d; vX = (tX - cp.x) * aSize / d; vY = (tY - cp.y) * aSize / d; } if (!color) switch (edgeColor) { case 'source': color = source.color || defaultNodeColor; break; case 'target': color = target.color || defaultNodeColor; break; default: color = defaultEdgeColor; break; } context.strokeStyle = color; context.lineWidth = size; context.beginPath(); context.moveTo(sX, sY); if (source.id === target.id) { context.bezierCurveTo(cp.x2, cp.y2, cp.x1, cp.y1, aX, aY); } else { context.quadraticCurveTo(cp.x, cp.y, aX, aY); } context.stroke(); context.fillStyle = color; context.beginPath(); context.moveTo(aX + vX, aY + vY); context.lineTo(aX + vY * 0.6, aY - vX * 0.6); context.lineTo(aX - vY * 0.6, aY + vX * 0.6); context.lineTo(aX + vX, aY + vY); context.closePath(); context.fill(); if(edge.sourceDotColor != undefined || edge.targetDotColor != undefined) { var dotOffset = edge.dotOffset || 3; var dotSize = edge.dotSize || 1; dotSize = size*dotSize; dotOffset = dotOffset*tSize; if(edge.sourceDotColor != undefined) { createDot(context, sX, sY, cp, tX, tY, dotOffset, dotSize, edge.sourceDotColor); } if (edge.targetDotColor != undefined){ createDot(context, tX, tY, cp, sX, sY, dotOffset, dotSize, edge.targetDotColor); } } }; function createDot(context, sX, sY, cp, tX, tY, offset, size, color) { context.beginPath(); context.fillStyle = color; var dot = getPointOnBezier(sX, sY, cp.x, cp.y, tX, tY, offset); context.arc(dot.x, dot.y, size * 3, 0, 2 * Math.PI, false); context.fill(); } function getQBezierValue(t, p1, p2, p3) { var iT = 1 - t; return iT * iT * p1 + 2 * iT * t * p2 + t * t * p3; } function getQuadraticCurvePoint(startX, startY, cpX, cpY, endX, endY, position) { return { x:getQBezierValue(position, startX, cpX, endX), y:getQBezierValue(position, startY, cpY, endY) }; } function getDistanceBetweenPoints(x1, y1, x2, y2){ return Math.sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1)); } /* Function to get a point on a bezier curve a certain distance away from its source. Needed since the position on a beziercurve is given to the formula as a percentage (t).*/ function getPointOnBezier(startX, startY, cpX, cpY, endX, endY, distance){ var bestT = 0; var bestAccuracy = 1000; var stepSize = 0.001; for(var t = 0; t<1; t+=stepSize){ var currentPoint = getQuadraticCurvePoint(startX, startY, cpX, cpY, endX, endY, t); var currentDistance = getDistanceBetweenPoints(startX, startY, currentPoint.x, currentPoint.y); if(Math.abs(currentDistance-distance) < bestAccuracy){ bestAccuracy = Math.abs(currentDistance-distance); bestT = t; } } return getQuadraticCurvePoint(startX, startY, cpX, cpY, endX, endY, bestT); } })();