Skip to content

Instantly share code, notes, and snippets.

@martinwairegi
Created March 27, 2023 05:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save martinwairegi/f4adfb02364b5dea0e05f9afe3b66dd8 to your computer and use it in GitHub Desktop.
Save martinwairegi/f4adfb02364b5dea0e05f9afe3b66dd8 to your computer and use it in GitHub Desktop.
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