From 7157df08af2c6ab57a403591b7d394916f88c1c9 Mon Sep 17 00:00:00 2001 From: wimrijnders Date: Sat, 9 Sep 2017 21:48:20 +0200 Subject: [PATCH] Network: Consolidate code for generating endpoints (#3422) * Moved endpoint-specific code to module EndPoints * Further decomposed functionality for endpoints * Added commenting, notably for typedef's * Add next attempt at fixing travis bug --- .../modules/components/edges/util/EdgeBase.js | 10 +- .../components/edges/util/EndPoints.js | 177 ++++++++++++++++++ lib/network/shapes.js | 50 ----- 3 files changed, 180 insertions(+), 57 deletions(-) create mode 100644 lib/network/modules/components/edges/util/EndPoints.js diff --git a/lib/network/modules/components/edges/util/EdgeBase.js b/lib/network/modules/components/edges/util/EdgeBase.js index fc96a3aa..d39fb8af 100644 --- a/lib/network/modules/components/edges/util/EdgeBase.js +++ b/lib/network/modules/components/edges/util/EdgeBase.js @@ -1,4 +1,6 @@ let util = require("../../../../../util"); +let EndPoints = require("./EndPoints").default; + /** * The Base Class for all edges. @@ -559,13 +561,7 @@ class EdgeBase { 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); - } + EndPoints.draw(ctx, arrowData); // draw shadow if enabled this.enableShadow(ctx, values); diff --git a/lib/network/modules/components/edges/util/EndPoints.js b/lib/network/modules/components/edges/util/EndPoints.js new file mode 100644 index 00000000..0bd1d178 --- /dev/null +++ b/lib/network/modules/components/edges/util/EndPoints.js @@ -0,0 +1,177 @@ +/** + * Location of all the endpoint drawing routines. + * + * Every endpoint has its own drawing routine, which contains an endpoint definition. + * + * The endpoint definitions must have the following properies: + * + * - (0,0) is the connection point to the node it attaches to + * - The endpoints are orientated to the positive x-direction + * - The length of the endpoint is at most 1 + * + * As long as the endpoint classes remain simple and not too numerous, they will be contained within this module. + * All classes here except `EndPoints` should be considered as private to this module. + */ + +// NOTE: When a typedef is isolated in a separate comment block, an actual description is generated for it, +// using the rest of the commenting in the code block. Usage of typedef in other comments then +// link to there. TIL. +// +// Also noteworthy, all typedef's set up in this manner are collected in a single, global page 'global.html'. +// In other words, it doesn't matter *where* the typedef's are defined in the code. +// +// +// TODO: add descriptive commenting to given typedef's + +/** + * @typedef {{type:string, point:Point, angle:number, length:number}} ArrowData + * + * Object containing instantiation data for a given endpoint. + */ + +/** + * @typedef {{x:number, y:number}} Point + * + * A point in view-coordinates. + */ + +/** + * Common methods for endpoints + * + * @class + */ +class EndPoint { + + /** + * Apply transformation on points for display. + * + * The following is done: + * - rotate by the specified angle + * - multiply the (normalized) coordinates by the passed length + * - offset by the target coordinates + * + * @param {Array} points + * @param {ArrowData} arrowData + * @static + */ + static transform(points, arrowData) { + if (!(points instanceof Array)) { + points = [points]; + } + + var x = arrowData.point.x; + var y = arrowData.point.y; + var angle = arrowData.angle + var length = arrowData.length; + + for(var i = 0; i < points.length; ++i) { + var p = points[i]; + var xt = p.x * Math.cos(angle) - p.y * Math.sin(angle); + var yt = p.x * Math.sin(angle) + p.y * Math.cos(angle); + + p.x = x + length*xt; + p.y = y + length*yt; + } + } + + + /** + * Draw a closed path using the given real coordinates. + * + * @param {CanvasRenderingContext2D} ctx + * @param {Array.} points + * @static + */ + static drawPath(ctx, points) { + ctx.beginPath(); + ctx.moveTo(points[0].x, points[0].y); + for(var i = 1; i < points.length; ++i) { + ctx.lineTo(points[i].x, points[i].y); + } + ctx.closePath(); + } +} + + + + +/** + * Drawing methods for the arrow endpoint. + * @extends EndPoint + */ +class Arrow extends EndPoint { + + /** + * Draw this shape at the end of a line. + * + * @param {CanvasRenderingContext2D} ctx + * @param {ArrowData} arrowData + * @static + */ + static draw(ctx, arrowData) { + // Normalized points of closed path, in the order that they should be drawn. + // (0, 0) is the attachment point, and the point around which should be rotated + var points = [ + { x: 0 , y: 0 }, + { x:-1 , y: 0.3}, + { x:-0.9, y: 0 }, + { x:-1 , y:-0.3}, + ]; + + EndPoint.transform(points, arrowData); + EndPoint.drawPath(ctx, points); + } +} + + +/** + * Drawing methods for the circle endpoint. + */ +class Circle { + + /** + * Draw this shape at the end of a line. + * + * @param {CanvasRenderingContext2D} ctx + * @param {ArrowData} arrowData + * @static + */ + static draw(ctx, arrowData) { + var point = {x:-0.4, y:0}; + + EndPoint.transform(point, arrowData); + ctx.circle(point.x, point.y, arrowData.length*0.4); + } +} + + +/** + * Drawing methods for the endpoints. + */ +class EndPoints { + + /** + * Draw an endpoint + * + * @param {CanvasRenderingContext2D} ctx + * @param {ArrowData} arrowData + * @static + */ + static draw(ctx, arrowData) { + var type; + if (arrowData.type) { + type = arrowData.type.toLowerCase(); + } + + switch (type) { + case 'circle': + Circle.draw(ctx, arrowData); + break; + case 'arrow': // fall-through + default: + Arrow.draw(ctx, arrowData); + } + } +} + +export default EndPoints; diff --git a/lib/network/shapes.js b/lib/network/shapes.js index 42489254..cbb5a8da 100644 --- a/lib/network/shapes.js +++ b/lib/network/shapes.js @@ -227,54 +227,6 @@ if (typeof CanvasRenderingContext2D !== 'undefined') { }; - /** - * Draw an arrow at the end of a line with the given angle. - * - * @param {number} x - * @param {number} y - * @param {number} angle - * @param {number} length - */ - CanvasRenderingContext2D.prototype.arrowEndpoint = function (x, y, angle, length) { - // tail - var xt = x - length * Math.cos(angle); - var yt = y - length * Math.sin(angle); - - // inner tail - var xi = x - length * 0.9 * Math.cos(angle); - var yi = y - length * 0.9 * Math.sin(angle); - - // left - var xl = xt + length / 3 * Math.cos(angle + 0.5 * Math.PI); - var yl = yt + length / 3 * Math.sin(angle + 0.5 * Math.PI); - - // right - var xr = xt + length / 3 * Math.cos(angle - 0.5 * Math.PI); - var yr = yt + length / 3 * Math.sin(angle - 0.5 * Math.PI); - - this.beginPath(); - this.moveTo(x, y); - this.lineTo(xl, yl); - this.lineTo(xi, yi); - this.lineTo(xr, yr); - this.closePath(); - }; - - /** - * Draw an circle an the end of an line with the given angle. - * - * @param {number} x - * @param {number} y - * @param {number} angle - * @param {number} length - */ - CanvasRenderingContext2D.prototype.circleEndpoint = function (x, y, angle, length) { - var radius = length * 0.4; - var xc = x - radius * Math.cos(angle); - var yc = y - radius * Math.sin(angle); - this.circle(xc, yc, radius); - }; - /** * Sets up the dashedLine functionality for drawing * Original code came from http://stackoverflow.com/questions/4576724/dotted-stroke-in-canvas @@ -336,6 +288,4 @@ if (typeof CanvasRenderingContext2D !== 'undefined') { } this.closePath(); }; - - }