This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Edge { | |
constructor(id, fromNode, toNode) { | |
this.id = id; | |
this.fromNode = fromNode; | |
this.toNode = toNode; | |
this.label = ""; | |
// Set if self loop | |
this.x = null; | |
this.y = null; | |
this.radius = null; | |
// Set if non self loop | |
this.angle = null; | |
// Set if curved | |
this.curved = false; | |
} | |
/** | |
* Draws edge to canvas | |
* @param {CanvasRenderingContext2D} ctx 2D rendering context for drawing surface of FSM canvas | |
*/ | |
draw(ctx) { | |
ctx.strokeStyle = BLACK; | |
ctx.fillStyle = BLACK; | |
// Colour edge red if highlighted | |
if (this.id == highTid) { | |
ctx.strokeStyle = RED; | |
ctx.fillStyle = RED; | |
} | |
ctx.beginPath(); | |
if (this.fromNode == this.toNode) { // self loop | |
this.angle = 5*Math.PI/16; | |
var dx = Math.cos(this.angle)*RADIUS; | |
var dy = Math.sin(this.angle)*RADIUS; | |
var xn = this.fromNode.x; | |
var yn = this.fromNode.y; | |
// Start of arc | |
var x1 = xn-dx; | |
var y1 = yn-dy; | |
// End of arc | |
var x2 = xn+dx; | |
var y2 = yn-dy; | |
// Arc turning point | |
var x3 = xn; | |
var y3 = yn-1.7*RADIUS; | |
// Find circle equation from three points (above) | |
var circle = circleFromPoints(x1, y1, x2, y2, x3, y3); | |
this.x = circle.x; // x centre | |
this.y = circle.y // y centre | |
this.radius = circle.radius; | |
// Angle between arc centre and end of arc | |
var alpha = Math.atan2(y2-this.y, x2-this.x); | |
ctx.beginPath(); | |
ctx.arc(this.x, this.y, this.radius, Math.PI-alpha, alpha); // arc is drawn outside of node area | |
ctx.stroke(); | |
// Draw chevron at end of arc | |
ctx.beginPath(); | |
ctx.moveTo(x2, y2); | |
ctx.lineTo(x2+CHEVRON*Math.cos(this.angle-Math.PI/10), y2-CHEVRON*Math.sin(this.angle-Math.PI/10)); | |
ctx.lineTo(x2-CHEVRON*Math.cos(this.angle+Math.PI/10), y2-CHEVRON*Math.sin(this.angle+Math.PI/10)); | |
ctx.closePath(); | |
ctx.stroke(); | |
ctx.fill(); | |
ctx.strokeStyle = BLACK; // revert colour to black | |
ctx.fillStyle = STATEFILL; | |
var width = ctx.measureText(this.label).width; | |
ctx.fillRect(x3-width/2, y3-4-FONTSIZE+2, width, FONTSIZE+2); | |
ctx.fillStyle = BLACK; | |
ctx.beginPath(); | |
ctx.fillText(this.label, x3, y3-4); | |
ctx.stroke(); | |
ctx.fillStyle = STATEFILL | |
} else if (this.curved) { // curved edge between nodes | |
var x1 = this.fromNode.x; | |
var y1 = this.fromNode.y; | |
var x2 = this.toNode.x; | |
var y2 = this.toNode.y; | |
var dx = x1-x2; | |
var dy = y1-y2; | |
this.angle = Math.atan2(dy, dx); | |
var x3 = 0.5*(x1+x2) + 2*SELECTAREA*Math.cos(this.angle - Math.PI/2); | |
var y3 = 0.5*(y1+y2) + 2*SELECTAREA*Math.sin(this.angle - Math.PI/2); | |
// create circle using three points | |
var circle = circleFromPoints(x1, y1, x2, y2, x3, y3); | |
var xc = circle.x; | |
var yc = circle.y; | |
// only draw section between nodes | |
var startAngle = Math.atan2(y2-yc, x2-xc); | |
var endAngle = Math.atan2(y1-yc, x1-xc); | |
ctx.beginPath(); | |
ctx.arc(xc, yc, circle.radius, startAngle, endAngle); | |
ctx.stroke(); | |
// get coords of arc intersection with 'to' node | |
var alpha = Math.acos(RADIUS/(2*circle.radius)) - startAngle + Math.PI; | |
var xi = x2 + RADIUS*Math.cos(alpha); | |
var yi = y2 - RADIUS*Math.sin(alpha); | |
var beta = Math.atan2(yi-y2,xi-x2); | |
// dynamically draw chevron | |
ctx.beginPath(); | |
ctx.moveTo(xi, yi); | |
ctx.lineTo(xi+CHEVRON*Math.cos(beta-Math.PI/5), yi+CHEVRON*Math.sin(beta-Math.PI/5)); | |
ctx.lineTo(xi+CHEVRON*Math.cos(beta+Math.PI/5), yi+CHEVRON*Math.sin(beta+Math.PI/5)); | |
ctx.closePath(); | |
ctx.stroke(); | |
ctx.fill(); | |
ctx.strokeStyle = BLACK; // revert colour to black | |
// draw the label at the third point that was created | |
ctx.fillStyle = STATEFILL; | |
var width = ctx.measureText(this.label).width; | |
ctx.fillRect(x3-width/2, y3-FONTSIZE+2, width, FONTSIZE+2); | |
ctx.fillStyle = BLACK; | |
ctx.beginPath(); | |
ctx.fillText(this.label, x3, y3); | |
ctx.stroke(); | |
ctx.fillStyle = STATEFILL; | |
} else { | |
if (this.id == startTid) { // start edge | |
var toX = this.toNode.x-RADIUS; | |
var toY = this.toNode.y; | |
var fromX = toX-RADIUS; | |
var fromY = toY; | |
var dx = RADIUS; | |
var dy = 0; | |
this.angle = Math.atan2(dy, dx); | |
} else { // edge between nodes | |
var toX = this.toNode.x; | |
var toY = this.toNode.y; | |
var fromX = this.fromNode.x; | |
var fromY = this.fromNode.y; | |
// Calculates line angle between centres of each node | |
var dx = toX-fromX; | |
var dy = toY-fromY; | |
this.angle = Math.atan2(dy, dx); | |
// 'Remove' portion of edge contained within nodes | |
fromX += Math.cos(this.angle)*RADIUS; | |
fromY += Math.sin(this.angle)*RADIUS; | |
toX -= Math.cos(this.angle)*RADIUS; | |
toY -= Math.sin(this.angle)*RADIUS; | |
} | |
// Draw connecting line | |
ctx.beginPath(); | |
ctx.moveTo(fromX, fromY); | |
ctx.lineTo(toX, toY); | |
ctx.stroke(); | |
// Draw chevron at end of edge | |
ctx.beginPath(); | |
ctx.moveTo(toX, toY); | |
ctx.lineTo(toX-CHEVRON*Math.cos(this.angle - Math.PI/6), toY-CHEVRON*Math.sin(this.angle - Math.PI/6)); | |
ctx.lineTo(toX-CHEVRON*Math.cos(this.angle + Math.PI/6), toY-CHEVRON*Math.sin(this.angle + Math.PI/6)); | |
ctx.closePath(); | |
ctx.stroke(); | |
ctx.fill(); | |
ctx.strokeStyle = BLACK; // revert colour to black | |
ctx.fillStyle = STATEFILL; | |
if (this.fromNode != null) { | |
var width = ctx.measureText(this.label).width; | |
var x = (this.fromNode.x + this.toNode.x) / 2; | |
var y = (this.fromNode.y + this.toNode.y) / 2; | |
ctx.fillRect(x-width/2, y-FONTSIZE+2, width, FONTSIZE+2); | |
ctx.fillStyle = BLACK; | |
ctx.beginPath(); | |
ctx.fillText(this.label, x, y); | |
ctx.stroke(); | |
ctx.fillStyle = STATEFILL; | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment