| @ -0,0 +1,386 @@ | |||
| var util = require('../../../util'); | |||
| import Label from './unified/label' | |||
| import Box from './nodes/box' | |||
| import Circle from './nodes/circle' | |||
| import CircularImage from './nodes/circularImage' | |||
| import Database from './nodes/database' | |||
| import Dot from './nodes/dot' | |||
| import Ellipse from './nodes/ellipse' | |||
| import Icon from './nodes/icon' | |||
| import Image from './nodes/image' | |||
| import Square from './nodes/square' | |||
| import Star from './nodes/star' | |||
| import Text from './nodes/text' | |||
| import Triangle from './nodes/triangle' | |||
| import TriangleDown from './nodes/triangleDown' | |||
| /** | |||
| * @class Node | |||
| * A node. A node can be connected to other nodes via one or multiple edges. | |||
| * @param {object} options An object containing options for the node. All | |||
| * options are optional, except for the id. | |||
| * {number} id Id of the node. Required | |||
| * {string} label Text label for the node | |||
| * {number} x Horizontal position of the node | |||
| * {number} y Vertical position of the node | |||
| * {string} shape Node shape, available: | |||
| * "database", "circle", "ellipse", | |||
| * "box", "image", "text", "dot", | |||
| * "star", "triangle", "triangleDown", | |||
| * "square", "icon" | |||
| * {string} image An image url | |||
| * {string} title An title text, can be HTML | |||
| * {anytype} group A group name or number | |||
| * @param {Network.Images} imagelist A list with images. Only needed | |||
| * when the node has an image | |||
| * @param {Network.Groups} grouplist A list with groups. Needed for | |||
| * retrieving group options | |||
| * @param {Object} constants An object with default values for | |||
| * example for the color | |||
| * | |||
| */ | |||
| class Node { | |||
| constructor(options, body, imagelist, grouplist, globalOptions) { | |||
| this.options = util.bridgeObject(globalOptions); | |||
| this.body = body; | |||
| this.selected = false; | |||
| this.hover = false; | |||
| this.edges = []; // all edges connected to this node | |||
| // set defaults for the options | |||
| this.id = undefined; | |||
| this.allowedToMoveX = false; | |||
| this.allowedToMoveY = false; | |||
| this.xFixed = false; | |||
| this.yFixed = false; | |||
| this.boundingBox = {top: 0, left: 0, right: 0, bottom: 0}; | |||
| this.imagelist = imagelist; | |||
| this.grouplist = grouplist; | |||
| // physics options | |||
| this.x = null; | |||
| this.y = null; | |||
| this.predefinedPosition = false; // used to check if initial zoomExtent should just take the range or approximate | |||
| this.fixedData = {x: null, y: null}; | |||
| this.labelModule = new Label(this.body, this.options); | |||
| this.setOptions(options); | |||
| } | |||
| /** | |||
| * Attach a edge to the node | |||
| * @param {Edge} edge | |||
| */ | |||
| attachEdge(edge) { | |||
| if (this.edges.indexOf(edge) == -1) { | |||
| this.edges.push(edge); | |||
| } | |||
| } | |||
| /** | |||
| * Detach a edge from the node | |||
| * @param {Edge} edge | |||
| */ | |||
| detachEdge(edge) { | |||
| var index = this.edges.indexOf(edge); | |||
| if (index != -1) { | |||
| this.edges.splice(index, 1); | |||
| } | |||
| } | |||
| /** | |||
| * Set or overwrite options for the node | |||
| * @param {Object} options an object with options | |||
| * @param {Object} constants and object with default, global options | |||
| */ | |||
| setOptions(options) { | |||
| if (!options) { | |||
| return; | |||
| } | |||
| var fields = [ | |||
| 'id', | |||
| 'borderWidth', | |||
| 'borderWidthSelected', | |||
| 'shape', | |||
| 'image', | |||
| 'brokenImage', | |||
| 'size', | |||
| 'label', | |||
| 'customScalingFunction', | |||
| 'icon', | |||
| 'value', | |||
| 'hidden', | |||
| 'physics' | |||
| ]; | |||
| util.selectiveDeepExtend(fields, this.options, options); | |||
| // basic options | |||
| if (options.id !== undefined) { | |||
| this.id = options.id; | |||
| } | |||
| if (options.title !== undefined) { | |||
| this.title = options.title; | |||
| } | |||
| if (options.x !== undefined) { | |||
| this.x = options.x; | |||
| this.predefinedPosition = true; | |||
| } | |||
| if (options.y !== undefined) { | |||
| this.y = options.y; | |||
| this.predefinedPosition = true; | |||
| } | |||
| if (options.value !== undefined) { | |||
| this.value = options.value; | |||
| } | |||
| if (options.level !== undefined) { | |||
| this.level = options.level; | |||
| this.preassignedLevel = true; | |||
| } | |||
| if (options.triggerFunction !== undefined) { | |||
| this.triggerFunction = options.triggerFunction; | |||
| } | |||
| if (this.id === undefined) { | |||
| throw "Node must have an id"; | |||
| } | |||
| // copy group options | |||
| if (typeof options.group === 'number' || (typeof options.group === 'string' && options.group != '')) { | |||
| var groupObj = this.grouplist.get(options.group); | |||
| util.deepExtend(this.options, groupObj); | |||
| // the color object needs to be completely defined. Since groups can partially overwrite the colors, we parse it again, just in case. | |||
| this.options.color = util.parseColor(this.options.color); | |||
| } | |||
| // individual shape options | |||
| if (options.color !== undefined) { | |||
| this.options.color = util.parseColor(options.color); | |||
| } | |||
| if (this.options.image !== undefined && this.options.image != "") { | |||
| if (this.imagelist) { | |||
| this.imageObj = this.imagelist.load(this.options.image, this.options.brokenImage); | |||
| } | |||
| else { | |||
| throw "No imagelist provided"; | |||
| } | |||
| } | |||
| if (options.allowedToMoveX !== undefined) { | |||
| this.xFixed = !options.allowedToMoveX; | |||
| this.allowedToMoveX = options.allowedToMoveX; | |||
| } | |||
| else if (options.x !== undefined && this.allowedToMoveX == false) { | |||
| this.xFixed = true; | |||
| } | |||
| if (options.allowedToMoveY !== undefined) { | |||
| this.yFixed = !options.allowedToMoveY; | |||
| this.allowedToMoveY = options.allowedToMoveY; | |||
| } | |||
| else if (options.y !== undefined && this.allowedToMoveY == false) { | |||
| this.yFixed = true; | |||
| } | |||
| // choose draw method depending on the shape | |||
| switch (this.options.shape) { | |||
| case 'database': | |||
| this.shape = new Database(this.options, this.body, this.labelModule); | |||
| break; | |||
| case 'box': | |||
| this.shape = new Box(this.options, this.body, this.labelModule); | |||
| break; | |||
| case 'circle': | |||
| this.shape = new Circle(this.options, this.body, this.labelModule); | |||
| break; | |||
| case 'ellipse': | |||
| this.shape = new Ellipse(this.options, this.body, this.labelModule); | |||
| break; | |||
| // TODO: add diamond shape | |||
| case 'image': | |||
| this.shape = new Image(this.options, this.body, this.labelModule, this.imageObj); | |||
| break; | |||
| case 'circularImage': | |||
| this.shape = new CircularImage(this.options, this.body, this.labelModule, this.imageObj); | |||
| break; | |||
| case 'text': | |||
| this.shape = new Text(this.options, this.body, this.labelModule); | |||
| break; | |||
| case 'dot': | |||
| this.shape = new Dot(this.options, this.body, this.labelModule); | |||
| break; | |||
| case 'square': | |||
| this.shape = new Square(this.options, this.body, this.labelModule); | |||
| break; | |||
| case 'triangle': | |||
| this.shape = new Triangle(this.options, this.body, this.labelModule); | |||
| break; | |||
| case 'triangleDown': | |||
| this.shape = new TriangleDown(this.options, this.body, this.labelModule); | |||
| break; | |||
| case 'star': | |||
| this.shape = new Star(this.options, this.body, this.labelModule); | |||
| break; | |||
| case 'icon': | |||
| this.shape = new Icon(this.options, this.body, this.labelModule); | |||
| break; | |||
| default: | |||
| this.shape = new Ellipse(this.options, this.body, this.labelModule); | |||
| break; | |||
| } | |||
| this.labelModule.setOptions(this.options); | |||
| // reset the size of the node, this can be changed | |||
| this._reset(); | |||
| } | |||
| /** | |||
| * select this node | |||
| */ | |||
| select() { | |||
| this.selected = true; | |||
| this._reset(); | |||
| } | |||
| /** | |||
| * unselect this node | |||
| */ | |||
| unselect() { | |||
| this.selected = false; | |||
| this._reset(); | |||
| } | |||
| /** | |||
| * Reset the calculated size of the node, forces it to recalculate its size | |||
| * @private | |||
| */ | |||
| _reset() { | |||
| this.width = undefined; | |||
| this.height = undefined; | |||
| } | |||
| /** | |||
| * get the title of this node. | |||
| * @return {string} title The title of the node, or undefined when no title | |||
| * has been set. | |||
| */ | |||
| getTitle() { | |||
| return typeof this.title === "function" ? this.title() : this.title; | |||
| } | |||
| /** | |||
| * Calculate the distance to the border of the Node | |||
| * @param {CanvasRenderingContext2D} ctx | |||
| * @param {Number} angle Angle in radians | |||
| * @returns {number} distance Distance to the border in pixels | |||
| */ | |||
| distanceToBorder(ctx, angle) { | |||
| this.shape.distanceToBorder(ctx,angle); | |||
| } | |||
| /** | |||
| * Check if this node has a fixed x and y position | |||
| * @return {boolean} true if fixed, false if not | |||
| */ | |||
| isFixed() { | |||
| return (this.xFixed && this.yFixed); | |||
| } | |||
| /** | |||
| * check if this node is selecte | |||
| * @return {boolean} selected True if node is selected, else false | |||
| */ | |||
| isSelected() { | |||
| return this.selected; | |||
| } | |||
| /** | |||
| * Retrieve the value of the node. Can be undefined | |||
| * @return {Number} value | |||
| */ | |||
| getValue() { | |||
| return this.value; | |||
| } | |||
| /** | |||
| * Adjust the value range of the node. The node will adjust it's size | |||
| * based on its value. | |||
| * @param {Number} min | |||
| * @param {Number} max | |||
| */ | |||
| setValueRange(min, max, total) { | |||
| if (this.value !== undefined) { | |||
| var scale = this.options.scaling.customScalingFunction(min, max, total, this.value); | |||
| var sizeDiff = this.options.scaling.max - this.options.scaling.min; | |||
| if (this.options.scaling.label.enabled == true) { | |||
| var fontDiff = this.options.scaling.label.max - this.options.scaling.label.min; | |||
| this.options.font.size = this.options.scaling.label.min + scale * fontDiff; | |||
| } | |||
| this.options.size = this.options.scaling.min + scale * sizeDiff; | |||
| } | |||
| } | |||
| /** | |||
| * Draw this node in the given canvas | |||
| * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); | |||
| * @param {CanvasRenderingContext2D} ctx | |||
| */ | |||
| draw(ctx) { | |||
| this.shape.draw(ctx, this.x, this.y, this.selected, this.hover); | |||
| } | |||
| /** | |||
| * Recalculate the size of this node in the given canvas | |||
| * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); | |||
| * @param {CanvasRenderingContext2D} ctx | |||
| */ | |||
| resize(ctx) { | |||
| this.shape.resize(ctx); | |||
| } | |||
| /** | |||
| * Check if this object is overlapping with the provided object | |||
| * @param {Object} obj an object with parameters left, top, right, bottom | |||
| * @return {boolean} True if location is located on node | |||
| */ | |||
| isOverlappingWith(obj) { | |||
| return ( | |||
| this.shape.left < obj.right && | |||
| this.shape.left + this.shape.width > obj.left && | |||
| this.shape.top < obj.bottom && | |||
| this.shape.top + this.shape.height > obj.top | |||
| ); | |||
| } | |||
| } | |||
| export default Node; | |||
| @ -1,881 +0,0 @@ | |||
| var util = require('../../../../util'); | |||
| import Label from '../unified/label.js' | |||
| /** | |||
| * @class Node | |||
| * A node. A node can be connected to other nodes via one or multiple edges. | |||
| * @param {object} options An object containing options for the node. All | |||
| * options are optional, except for the id. | |||
| * {number} id Id of the node. Required | |||
| * {string} label Text label for the node | |||
| * {number} x Horizontal position of the node | |||
| * {number} y Vertical position of the node | |||
| * {string} shape Node shape, available: | |||
| * "database", "circle", "ellipse", | |||
| * "box", "image", "text", "dot", | |||
| * "star", "triangle", "triangleDown", | |||
| * "square", "icon" | |||
| * {string} image An image url | |||
| * {string} title An title text, can be HTML | |||
| * {anytype} group A group name or number | |||
| * @param {Network.Images} imagelist A list with images. Only needed | |||
| * when the node has an image | |||
| * @param {Network.Groups} grouplist A list with groups. Needed for | |||
| * retrieving group options | |||
| * @param {Object} constants An object with default values for | |||
| * example for the color | |||
| * | |||
| */ | |||
| class Node { | |||
| constructor(options, body, imagelist, grouplist, globalOptions) { | |||
| this.options = util.bridgeObject(globalOptions); | |||
| this.selected = false; | |||
| this.hover = false; | |||
| this.edges = []; // all edges connected to this node | |||
| // set defaults for the options | |||
| this.id = undefined; | |||
| this.allowedToMoveX = false; | |||
| this.allowedToMoveY = false; | |||
| this.xFixed = false; | |||
| this.yFixed = false; | |||
| this.boundingBox = {top: 0, left: 0, right: 0, bottom: 0}; | |||
| this.imagelist = imagelist; | |||
| this.grouplist = grouplist; | |||
| // physics options | |||
| this.x = null; | |||
| this.y = null; | |||
| this.predefinedPosition = false; // used to check if initial zoomExtent should just take the range or approximate | |||
| this.fixedData = {x: null, y: null}; | |||
| this.labelModule = new Label(body, this.options); | |||
| this.setOptions(options); | |||
| } | |||
| /** | |||
| * Attach a edge to the node | |||
| * @param {Edge} edge | |||
| */ | |||
| attachEdge(edge) { | |||
| if (this.edges.indexOf(edge) == -1) { | |||
| this.edges.push(edge); | |||
| } | |||
| } | |||
| /** | |||
| * Detach a edge from the node | |||
| * @param {Edge} edge | |||
| */ | |||
| detachEdge(edge) { | |||
| var index = this.edges.indexOf(edge); | |||
| if (index != -1) { | |||
| this.edges.splice(index, 1); | |||
| } | |||
| } | |||
| /** | |||
| * Set or overwrite options for the node | |||
| * @param {Object} options an object with options | |||
| * @param {Object} constants and object with default, global options | |||
| */ | |||
| setOptions(options) { | |||
| if (!options) { | |||
| return; | |||
| } | |||
| var fields = [ | |||
| 'borderWidth', | |||
| 'borderWidthSelected', | |||
| 'shape', | |||
| 'image', | |||
| 'brokenImage', | |||
| 'size', | |||
| 'label', | |||
| 'customScalingFunction', | |||
| 'icon', | |||
| 'value', | |||
| 'hidden', | |||
| 'physics' | |||
| ]; | |||
| util.selectiveDeepExtend(fields, this.options, options); | |||
| // basic options | |||
| if (options.id !== undefined) { | |||
| this.id = options.id; | |||
| } | |||
| if (options.title !== undefined) { | |||
| this.title = options.title; | |||
| } | |||
| if (options.x !== undefined) { | |||
| this.x = options.x; | |||
| this.predefinedPosition = true; | |||
| } | |||
| if (options.y !== undefined) { | |||
| this.y = options.y; | |||
| this.predefinedPosition = true; | |||
| } | |||
| if (options.value !== undefined) { | |||
| this.value = options.value; | |||
| } | |||
| if (options.level !== undefined) { | |||
| this.level = options.level; | |||
| this.preassignedLevel = true; | |||
| } | |||
| if (options.triggerFunction !== undefined) { | |||
| this.triggerFunction = options.triggerFunction; | |||
| } | |||
| if (this.id === undefined) { | |||
| throw "Node must have an id"; | |||
| } | |||
| // copy group options | |||
| if (typeof options.group === 'number' || (typeof options.group === 'string' && options.group != '')) { | |||
| var groupObj = this.grouplist.get(options.group); | |||
| util.deepExtend(this.options, groupObj); | |||
| // the color object needs to be completely defined. Since groups can partially overwrite the colors, we parse it again, just in case. | |||
| this.options.color = util.parseColor(this.options.color); | |||
| } | |||
| // individual shape options | |||
| if (options.color !== undefined) { | |||
| this.options.color = util.parseColor(options.color); | |||
| } | |||
| if (this.options.image !== undefined && this.options.image != "") { | |||
| if (this.imagelist) { | |||
| this.imageObj = this.imagelist.load(this.options.image, this.options.brokenImage); | |||
| } | |||
| else { | |||
| throw "No imagelist provided"; | |||
| } | |||
| } | |||
| if (options.allowedToMoveX !== undefined) { | |||
| this.xFixed = !options.allowedToMoveX; | |||
| this.allowedToMoveX = options.allowedToMoveX; | |||
| } | |||
| else if (options.x !== undefined && this.allowedToMoveX == false) { | |||
| this.xFixed = true; | |||
| } | |||
| if (options.allowedToMoveY !== undefined) { | |||
| this.yFixed = !options.allowedToMoveY; | |||
| this.allowedToMoveY = options.allowedToMoveY; | |||
| } | |||
| else if (options.y !== undefined && this.allowedToMoveY == false) { | |||
| this.yFixed = true; | |||
| } | |||
| // choose draw method depending on the shape | |||
| switch (this.options.shape) { | |||
| case 'database': | |||
| this.draw = this._drawDatabase; | |||
| this.resize = this._resizeDatabase; | |||
| break; | |||
| case 'box': | |||
| this.draw = this._drawBox; | |||
| this.resize = this._resizeBox; | |||
| break; | |||
| case 'circle': | |||
| this.draw = this._drawCircle; | |||
| this.resize = this._resizeCircle; | |||
| break; | |||
| case 'ellipse': | |||
| this.draw = this._drawEllipse; | |||
| this.resize = this._resizeEllipse; | |||
| break; | |||
| // TODO: add diamond shape | |||
| case 'image': | |||
| this.draw = this._drawImage; | |||
| this.resize = this._resizeImage; | |||
| break; | |||
| case 'circularImage': | |||
| this.draw = this._drawCircularImage; | |||
| this.resize = this._resizeCircularImage; | |||
| break; | |||
| case 'text': | |||
| this.draw = this._drawText; | |||
| this.resize = this._resizeText; | |||
| break; | |||
| case 'dot': | |||
| this.draw = this._drawDot; | |||
| this.resize = this._resizeShape; | |||
| break; | |||
| case 'square': | |||
| this.draw = this._drawSquare; | |||
| this.resize = this._resizeShape; | |||
| break; | |||
| case 'triangle': | |||
| this.draw = this._drawTriangle; | |||
| this.resize = this._resizeShape; | |||
| break; | |||
| case 'triangleDown': | |||
| this.draw = this._drawTriangleDown; | |||
| this.resize = this._resizeShape; | |||
| break; | |||
| case 'star': | |||
| this.draw = this._drawStar; | |||
| this.resize = this._resizeShape; | |||
| break; | |||
| case 'icon': | |||
| this.draw = this._drawIcon; | |||
| this.resize = this._resizeIcon; | |||
| break; | |||
| default: | |||
| this.draw = this._drawEllipse; | |||
| this.resize = this._resizeEllipse; | |||
| break; | |||
| } | |||
| this.labelModule.setOptions(this.options); | |||
| // reset the size of the node, this can be changed | |||
| this._reset(); | |||
| } | |||
| /** | |||
| * select this node | |||
| */ | |||
| select() { | |||
| this.selected = true; | |||
| this._reset(); | |||
| } | |||
| /** | |||
| * unselect this node | |||
| */ | |||
| unselect() { | |||
| this.selected = false; | |||
| this._reset(); | |||
| } | |||
| /** | |||
| * Reset the calculated size of the node, forces it to recalculate its size | |||
| * @private | |||
| */ | |||
| _reset() { | |||
| this.width = undefined; | |||
| this.height = undefined; | |||
| } | |||
| /** | |||
| * get the title of this node. | |||
| * @return {string} title The title of the node, or undefined when no title | |||
| * has been set. | |||
| */ | |||
| getTitle() { | |||
| return typeof this.title === "function" ? this.title() : this.title; | |||
| } | |||
| /** | |||
| * Calculate the distance to the border of the Node | |||
| * @param {CanvasRenderingContext2D} ctx | |||
| * @param {Number} angle Angle in radians | |||
| * @returns {number} distance Distance to the border in pixels | |||
| */ | |||
| distanceToBorder(ctx, angle) { | |||
| var borderWidth = 1; | |||
| if (!this.width) { | |||
| this.resize(ctx); | |||
| } | |||
| switch (this.options.shape) { | |||
| case 'circle': | |||
| case 'dot': | |||
| return this.options.size + borderWidth; | |||
| case 'ellipse': | |||
| var a = this.width / 2; | |||
| var b = this.height / 2; | |||
| var w = (Math.sin(angle) * a); | |||
| var h = (Math.cos(angle) * b); | |||
| return a * b / Math.sqrt(w * w + h * h); | |||
| // TODO: implement distanceToBorder for database | |||
| // TODO: implement distanceToBorder for triangle | |||
| // TODO: implement distanceToBorder for triangleDown | |||
| case 'box': | |||
| case 'image': | |||
| case 'text': | |||
| default: | |||
| if (this.width) { | |||
| return Math.min( | |||
| Math.abs(this.width / 2 / Math.cos(angle)), | |||
| Math.abs(this.height / 2 / Math.sin(angle))) + borderWidth; | |||
| // TODO: reckon with border size too in case of box | |||
| } | |||
| else { | |||
| return 0; | |||
| } | |||
| } | |||
| // TODO: implement calculation of distance to border for all shapes | |||
| } | |||
| /** | |||
| * Check if this node has a fixed x and y position | |||
| * @return {boolean} true if fixed, false if not | |||
| */ | |||
| isFixed() { | |||
| return (this.xFixed && this.yFixed); | |||
| } | |||
| /** | |||
| * check if this node is selecte | |||
| * @return {boolean} selected True if node is selected, else false | |||
| */ | |||
| isSelected() { | |||
| return this.selected; | |||
| } | |||
| /** | |||
| * Retrieve the value of the node. Can be undefined | |||
| * @return {Number} value | |||
| */ | |||
| getValue() { | |||
| return this.value; | |||
| } | |||
| /** | |||
| * Adjust the value range of the node. The node will adjust it's size | |||
| * based on its value. | |||
| * @param {Number} min | |||
| * @param {Number} max | |||
| */ | |||
| setValueRange(min, max, total) { | |||
| if (this.value !== undefined) { | |||
| var scale = this.options.scaling.customScalingFunction(min, max, total, this.value); | |||
| var sizeDiff = this.options.scaling.max - this.options.scaling.min; | |||
| if (this.options.scaling.label.enabled == true) { | |||
| var fontDiff = this.options.scaling.label.max - this.options.scaling.label.min; | |||
| this.options.font.size = this.options.scaling.label.min + scale * fontDiff; | |||
| } | |||
| this.options.size = this.options.scaling.min + scale * sizeDiff; | |||
| } | |||
| } | |||
| /** | |||
| * Draw this node in the given canvas | |||
| * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); | |||
| * @param {CanvasRenderingContext2D} ctx | |||
| */ | |||
| draw(ctx) { | |||
| throw "Draw method not initialized for node"; | |||
| } | |||
| /** | |||
| * Recalculate the size of this node in the given canvas | |||
| * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); | |||
| * @param {CanvasRenderingContext2D} ctx | |||
| */ | |||
| resize(ctx) { | |||
| throw "Resize method not initialized for node"; | |||
| } | |||
| /** | |||
| * Check if this object is overlapping with the provided object | |||
| * @param {Object} obj an object with parameters left, top, right, bottom | |||
| * @return {boolean} True if location is located on node | |||
| */ | |||
| isOverlappingWith(obj) { | |||
| return (this.left < obj.right && | |||
| this.left + this.width > obj.left && | |||
| this.top < obj.bottom && | |||
| this.top + this.height > obj.top); | |||
| } | |||
| _resizeImage(ctx) { | |||
| // TODO: pre calculate the image size | |||
| if (!this.width || !this.height) { // undefined or 0 | |||
| var width, height; | |||
| if (this.value) { | |||
| var scale = this.imageObj.height / this.imageObj.width; | |||
| if (scale !== undefined) { | |||
| width = this.options.size || this.imageObj.width; | |||
| height = this.options.size * scale || this.imageObj.height; | |||
| } | |||
| else { | |||
| width = 0; | |||
| height = 0; | |||
| } | |||
| } | |||
| else { | |||
| width = this.imageObj.width; | |||
| height = this.imageObj.height; | |||
| } | |||
| this.width = width; | |||
| this.height = height; | |||
| } | |||
| } | |||
| _drawImageAtPosition(ctx) { | |||
| if (this.imageObj.width != 0) { | |||
| // draw the image | |||
| ctx.globalAlpha = 1.0; | |||
| ctx.drawImage(this.imageObj, this.left, this.top, this.width, this.height); | |||
| } | |||
| } | |||
| _drawImageLabel(ctx) { | |||
| var yLabel; | |||
| var offset = 0; | |||
| if (this.height) { | |||
| offset = this.height / 2; | |||
| var labelDimensions = this.labelModule.labelDimensions; | |||
| if (labelDimensions.lineCount >= 1) { | |||
| offset += labelDimensions.height / 2; | |||
| offset += 3; | |||
| } | |||
| } | |||
| yLabel = this.y + offset; | |||
| this.labelModule.draw(ctx, this.x, yLabel); | |||
| } | |||
| _drawImage(ctx) { | |||
| this._resizeImage(ctx); | |||
| this.left = this.x - this.width / 2; | |||
| this.top = this.y - this.height / 2; | |||
| this._drawImageAtPosition(ctx); | |||
| this.boundingBox.top = this.top; | |||
| this.boundingBox.left = this.left; | |||
| this.boundingBox.right = this.left + this.width; | |||
| this.boundingBox.bottom = this.top + this.height; | |||
| this._drawImageLabel(ctx); | |||
| this.boundingBox.left = Math.min(this.boundingBox.left, this.labelModule.size.left); | |||
| this.boundingBox.right = Math.max(this.boundingBox.right, this.labelModule.size.left + this.labelModule.size.width); | |||
| this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelModule.size.height); | |||
| } | |||
| _resizeCircularImage(ctx) { | |||
| if (!this.imageObj.src || !this.imageObj.width || !this.imageObj.height) { | |||
| if (!this.width) { | |||
| var diameter = this.options.size * 2; | |||
| this.width = diameter; | |||
| this.height = diameter; | |||
| this._swapToImageResizeWhenImageLoaded = true; | |||
| } | |||
| } | |||
| else { | |||
| if (this._swapToImageResizeWhenImageLoaded) { | |||
| this.width = 0; | |||
| this.height = 0; | |||
| delete this._swapToImageResizeWhenImageLoaded; | |||
| } | |||
| this._resizeImage(ctx); | |||
| } | |||
| } | |||
| _drawCircularImage(ctx) { | |||
| this._resizeCircularImage(ctx); | |||
| this.left = this.x - this.width / 2; | |||
| this.top = this.y - this.height / 2; | |||
| var centerX = this.left + (this.width / 2); | |||
| var centerY = this.top + (this.height / 2); | |||
| var size = Math.abs(this.height / 2); | |||
| this._drawRawCircle(ctx, size); | |||
| ctx.save(); | |||
| ctx.circle(this.x, this.y, size); | |||
| ctx.stroke(); | |||
| ctx.clip(); | |||
| this._drawImageAtPosition(ctx); | |||
| ctx.restore(); | |||
| this.boundingBox.top = this.y - this.options.size; | |||
| this.boundingBox.left = this.x - this.options.size; | |||
| this.boundingBox.right = this.x + this.options.size; | |||
| this.boundingBox.bottom = this.y + this.options.size; | |||
| this._drawImageLabel(ctx); | |||
| this.boundingBox.left = Math.min(this.boundingBox.left, this.labelModule.size.left); | |||
| this.boundingBox.right = Math.max(this.boundingBox.right, this.labelModule.size.left + this.labelModule.size.width); | |||
| this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelModule.size.height); | |||
| } | |||
| _resizeBox(ctx) { | |||
| if (!this.width) { | |||
| var margin = 5; | |||
| var textSize = this.labelModule.getTextSize(ctx,this.selected); | |||
| this.width = textSize.width + 2 * margin; | |||
| this.height = textSize.height + 2 * margin; | |||
| } | |||
| } | |||
| _drawBox(ctx) { | |||
| this._resizeBox(ctx); | |||
| this.left = this.x - this.width / 2; | |||
| this.top = this.y - this.height / 2; | |||
| var borderWidth = this.options.borderWidth; | |||
| var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth; | |||
| ctx.strokeStyle = this.selected ? this.options.color.highlight.border : this.hover ? this.options.color.hover.border : this.options.color.border; | |||
| ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth); | |||
| ctx.lineWidth *= this.networkScaleInv; | |||
| ctx.lineWidth = Math.min(this.width, ctx.lineWidth); | |||
| ctx.fillStyle = this.selected ? this.options.color.highlight.background : this.hover ? this.options.color.hover.background : this.options.color.background; | |||
| ctx.roundRect(this.left, this.top, this.width, this.height, this.options.size); | |||
| ctx.fill(); | |||
| ctx.stroke(); | |||
| this.boundingBox.top = this.top; | |||
| this.boundingBox.left = this.left; | |||
| this.boundingBox.right = this.left + this.width; | |||
| this.boundingBox.bottom = this.top + this.height; | |||
| this.labelModule.draw(ctx, this.x, this.y); | |||
| } | |||
| _resizeDatabase(ctx) { | |||
| if (!this.width) { | |||
| var margin = 5; | |||
| var textSize = this.labelModule.getTextSize(ctx,this.selected); | |||
| var size = textSize.width + 2 * margin; | |||
| this.width = size; | |||
| this.height = size; | |||
| } | |||
| } | |||
| _drawDatabase(ctx) { | |||
| this._resizeDatabase(ctx); | |||
| this.left = this.x - this.width / 2; | |||
| this.top = this.y - this.height / 2; | |||
| var borderWidth = this.options.borderWidth; | |||
| var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth; | |||
| ctx.strokeStyle = this.selected ? this.options.color.highlight.border : this.hover ? this.options.color.hover.border : this.options.color.border; | |||
| ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth); | |||
| ctx.lineWidth *= this.networkScaleInv; | |||
| ctx.lineWidth = Math.min(this.width, ctx.lineWidth); | |||
| ctx.fillStyle = this.selected ? this.options.color.highlight.background : this.hover ? this.options.color.hover.background : this.options.color.background; | |||
| ctx.database(this.x - this.width / 2, this.y - this.height * 0.5, this.width, this.height); | |||
| ctx.fill(); | |||
| ctx.stroke(); | |||
| this.boundingBox.top = this.top; | |||
| this.boundingBox.left = this.left; | |||
| this.boundingBox.right = this.left + this.width; | |||
| this.boundingBox.bottom = this.top + this.height; | |||
| this.labelModule.draw(ctx, this.x, this.y); | |||
| } | |||
| _resizeCircle(ctx) { | |||
| if (!this.width) { | |||
| var margin = 5; | |||
| var textSize = this.labelModule.getTextSize(ctx,this.selected); | |||
| var diameter = Math.max(textSize.width, textSize.height) + 2 * margin; | |||
| this.options.size = diameter / 2; | |||
| this.width = diameter; | |||
| this.height = diameter; | |||
| } | |||
| } | |||
| _drawRawCircle(ctx, size) { | |||
| var borderWidth = this.options.borderWidth; | |||
| var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth; | |||
| ctx.strokeStyle = this.selected ? this.options.color.highlight.border : this.hover ? this.options.color.hover.border : this.options.color.border; | |||
| ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth); | |||
| ctx.lineWidth *= this.networkScaleInv; | |||
| ctx.lineWidth = Math.min(this.width, ctx.lineWidth); | |||
| ctx.fillStyle = this.selected ? this.options.color.highlight.background : this.hover ? this.options.color.hover.background : this.options.color.background; | |||
| ctx.circle(this.x, this.y, size); | |||
| ctx.fill(); | |||
| ctx.stroke(); | |||
| } | |||
| _drawCircle(ctx) { | |||
| this._resizeCircle(ctx); | |||
| this.left = this.x - this.width / 2; | |||
| this.top = this.y - this.height / 2; | |||
| this._drawRawCircle(ctx, this.options.size); | |||
| this.boundingBox.top = this.y - this.options.size; | |||
| this.boundingBox.left = this.x - this.options.size; | |||
| this.boundingBox.right = this.x + this.options.size; | |||
| this.boundingBox.bottom = this.y + this.options.size; | |||
| this.labelModule.draw(ctx, this.x, this.y); | |||
| } | |||
| _resizeEllipse(ctx) { | |||
| if (this.width === undefined) { | |||
| var textSize = this.labelModule.getTextSize(ctx,this.selected); | |||
| this.width = textSize.width * 1.5; | |||
| this.height = textSize.height * 2; | |||
| if (this.width < this.height) { | |||
| this.width = this.height; | |||
| } | |||
| } | |||
| } | |||
| _drawEllipse(ctx) { | |||
| this._resizeEllipse(ctx); | |||
| this.left = this.x - this.width / 2; | |||
| this.top = this.y - this.height / 2; | |||
| var borderWidth = this.options.borderWidth; | |||
| var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth; | |||
| ctx.strokeStyle = this.selected ? this.options.color.highlight.border : this.hover ? this.options.color.hover.border : this.options.color.border; | |||
| ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth); | |||
| ctx.lineWidth *= this.networkScaleInv; | |||
| ctx.lineWidth = Math.min(this.width, ctx.lineWidth); | |||
| ctx.fillStyle = this.selected ? this.options.color.highlight.background : this.hover ? this.options.color.hover.background : this.options.color.background; | |||
| ctx.ellipse(this.left, this.top, this.width, this.height); | |||
| ctx.fill(); | |||
| ctx.stroke(); | |||
| this.boundingBox.top = this.top; | |||
| this.boundingBox.left = this.left; | |||
| this.boundingBox.right = this.left + this.width; | |||
| this.boundingBox.bottom = this.top + this.height; | |||
| this.labelModule.draw(ctx, this.x, this.y, this.selected); | |||
| } | |||
| _drawDot(ctx) { | |||
| this._drawShape(ctx, 'circle'); | |||
| } | |||
| _drawTriangle(ctx) { | |||
| this._drawShape(ctx, 'triangle'); | |||
| } | |||
| _drawTriangleDown(ctx) { | |||
| this._drawShape(ctx, 'triangleDown'); | |||
| } | |||
| _drawSquare(ctx) { | |||
| this._drawShape(ctx, 'square'); | |||
| } | |||
| _drawStar(ctx) { | |||
| this._drawShape(ctx, 'star'); | |||
| } | |||
| _resizeShape(ctx) { | |||
| if (!this.width) { | |||
| var size = 2 * this.options.size; | |||
| this.width = size; | |||
| this.height = size; | |||
| } | |||
| } | |||
| _drawShape(ctx, shape) { | |||
| this._resizeShape(ctx); | |||
| this.left = this.x - this.width / 2; | |||
| this.top = this.y - this.height / 2; | |||
| var borderWidth = this.options.borderWidth; | |||
| var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth; | |||
| var sizeMultiplier = 2; | |||
| // choose draw method depending on the shape | |||
| switch (shape) { | |||
| case 'dot': | |||
| sizeMultiplier = 2; | |||
| break; | |||
| case 'square': | |||
| sizeMultiplier = 2; | |||
| break; | |||
| case 'triangle': | |||
| sizeMultiplier = 3; | |||
| break; | |||
| case 'triangleDown': | |||
| sizeMultiplier = 3; | |||
| break; | |||
| case 'star': | |||
| sizeMultiplier = 4; | |||
| break; | |||
| } | |||
| ctx.strokeStyle = this.selected ? this.options.color.highlight.border : this.hover ? this.options.color.hover.border : this.options.color.border; | |||
| ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth); | |||
| ctx.lineWidth *= this.networkScaleInv; | |||
| ctx.lineWidth = Math.min(this.width, ctx.lineWidth); | |||
| ctx.fillStyle = this.selected ? this.options.color.highlight.background : this.hover ? this.options.color.hover.background : this.options.color.background; | |||
| ctx[shape](this.x, this.y, this.options.size); | |||
| ctx.fill(); | |||
| ctx.stroke(); | |||
| this.boundingBox.top = this.y - this.options.size; | |||
| this.boundingBox.left = this.x - this.options.size; | |||
| this.boundingBox.right = this.x + this.options.size; | |||
| this.boundingBox.bottom = this.y + this.options.size; | |||
| if (this.options.label!== undefined) { | |||
| this.labelModule.draw(ctx, this.x, this.y + (this.height + this.labelModule.size.height)*0.5, this.selected, 'hanging'); | |||
| this.boundingBox.left = Math.min(this.boundingBox.left, this.labelModule.size.left); | |||
| this.boundingBox.right = Math.max(this.boundingBox.right, this.labelModule.size.left + this.labelModule.size.width); | |||
| this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelModule.size.height); | |||
| } | |||
| } | |||
| _resizeText(ctx) { | |||
| if (!this.width) { | |||
| var margin = 5; | |||
| var textSize = this.labelModule.getTextSize(ctx,this.selected); | |||
| this.width = textSize.width + 2 * margin; | |||
| this.height = textSize.height + 2 * margin; | |||
| } | |||
| } | |||
| _drawText(ctx) { | |||
| this._resizeText(ctx); | |||
| this.left = this.x - this.width / 2; | |||
| this.top = this.y - this.height / 2; | |||
| this.labelModule.draw(ctx, this.x, this.y); | |||
| this.boundingBox.top = this.top; | |||
| this.boundingBox.left = this.left; | |||
| this.boundingBox.right = this.left + this.width; | |||
| this.boundingBox.bottom = this.top + this.height; | |||
| } | |||
| _resizeIcon(ctx) { | |||
| if (!this.width) { | |||
| var margin = 5; | |||
| var iconSize = | |||
| { | |||
| width: Number(this.options.icon.iconSize), | |||
| height: Number(this.options.icon.iconSize) | |||
| }; | |||
| this.width = iconSize.width + 2 * margin; | |||
| this.height = iconSize.height + 2 * margin; | |||
| } | |||
| } | |||
| _drawIcon(ctx) { | |||
| this._resizeIcon(ctx); | |||
| this.options.icon.iconSize = this.options.icon.iconSize || 50; | |||
| this.left = this.x - this.width / 2; | |||
| this.top = this.y - this.height / 2; | |||
| this._icon(ctx); | |||
| this.boundingBox.top = this.y - this.options.icon.iconSize / 2; | |||
| this.boundingBox.left = this.x - this.options.icon.iconSize / 2; | |||
| this.boundingBox.right = this.x + this.options.icon.iconSize / 2; | |||
| this.boundingBox.bottom = this.y + this.options.icon.iconSize / 2; | |||
| if (this.options.label!== unde) { | |||
| var iconTextSpacing = 5; | |||
| this.labelModule.draw(ctx, this.x, this.y + this.height / 2 + iconTextSpacing); | |||
| this.boundingBox.left = Math.min(this.boundingBox.left, this.labelModule.size.left); | |||
| this.boundingBox.right = Math.max(this.boundingBox.right, this.labelModule.size.left + this.labelModule.size.width); | |||
| this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelModule.size.height); | |||
| } | |||
| } | |||
| _icon(ctx) { | |||
| var relativeIconSize = Number(this.options.icon.iconSize) * this.networkScale; | |||
| if (this.options.icon.code && relativeIconSize > this.options.scaling.label.drawThreshold - 1) { | |||
| var iconSize = Number(this.options.icon.iconSize); | |||
| ctx.font = (this.selected ? "bold " : "") + iconSize + "px " + this.options.icon.iconFontFace; | |||
| // draw icon | |||
| ctx.fillStyle = this.options.icon.iconColor || "black"; | |||
| ctx.textAlign = "center"; | |||
| ctx.textBaseline = "middle"; | |||
| ctx.fillText(this.options.icon.code, this.x, this.y); | |||
| } | |||
| } | |||
| } | |||
| export default Node; | |||
| @ -0,0 +1,68 @@ | |||
| /** | |||
| * Created by Alex on 3/18/2015. | |||
| */ | |||
| 'use strict'; | |||
| class Box { | |||
| constructor (options, body, labelModule) { | |||
| this.body = body; | |||
| this.labelModule = labelModule; | |||
| this.setOptions(options); | |||
| this.top = undefined; | |||
| this.left = undefined; | |||
| this.height = undefined; | |||
| this.height = undefined; | |||
| this.boundingBox = {top: 0, left: 0, right: 0, bottom: 0}; | |||
| } | |||
| setOptions(options) { | |||
| this.options = options; | |||
| } | |||
| resize(ctx) { | |||
| if (this.width === undefined) { | |||
| var margin = 5; | |||
| var textSize = this.labelModule.getTextSize(ctx,this.selected); | |||
| this.width = textSize.width + 2 * margin; | |||
| this.height = textSize.height + 2 * margin; | |||
| } | |||
| } | |||
| draw(ctx, x, y, selected, hover) { | |||
| this.resize(ctx); | |||
| this.left = x - this.width / 2; | |||
| this.top = y - this.height / 2; | |||
| var borderWidth = this.options.borderWidth; | |||
| var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth; | |||
| console.log(this) | |||
| ctx.strokeStyle = selected ? this.options.color.highlight.border : hover ? this.options.color.hover.border : this.options.color.border; | |||
| ctx.lineWidth = (selected ? selectionLineWidth : borderWidth); | |||
| ctx.lineWidth /= this.body.view.scale; | |||
| ctx.lineWidth = Math.min(this.width, ctx.lineWidth); | |||
| ctx.fillStyle = selected ? this.options.color.highlight.background : hover ? this.options.color.hover.background : this.options.color.background; | |||
| ctx.roundRect(this.left, this.top, this.width, this.height, this.options.size); | |||
| ctx.fill(); | |||
| ctx.stroke(); | |||
| this.boundingBox.top = this.top; | |||
| this.boundingBox.left = this.left; | |||
| this.boundingBox.right = this.left + this.width; | |||
| this.boundingBox.bottom = this.top + this.height; | |||
| this.labelModule.draw(ctx, x, y, selected); | |||
| } | |||
| distanceToBorder(ctx, angle) { | |||
| this.resize(ctx); | |||
| var a = this.width / 2; | |||
| var b = this.height / 2; | |||
| var w = (Math.sin(angle) * a); | |||
| var h = (Math.cos(angle) * b); | |||
| return a * b / Math.sqrt(w * w + h * h); | |||
| } | |||
| } | |||
| export default Box; | |||
| @ -0,0 +1,60 @@ | |||
| /** | |||
| * Created by Alex on 3/18/2015. | |||
| */ | |||
| 'use strict'; | |||
| import NodeUtil from './nodeUtil' | |||
| class Circle extends NodeUtil { | |||
| constructor (options, labelModule) { | |||
| this.labelModule = labelModule; | |||
| this.setOptions(options); | |||
| this.top = undefined; | |||
| this.left = undefined; | |||
| this.height = undefined; | |||
| this.height = undefined; | |||
| this.boundingBox = {top: 0, left: 0, right: 0, bottom: 0}; | |||
| } | |||
| setOptions(options) { | |||
| this.options = options; | |||
| } | |||
| resize(ctx) { | |||
| if (this.width === undefined) { | |||
| var margin = 5; | |||
| var textSize = this.labelModule.getTextSize(ctx,this.selected); | |||
| var diameter = Math.max(textSize.width, textSize.height) + 2 * margin; | |||
| this.options.size = diameter / 2; | |||
| this.width = diameter; | |||
| this.height = diameter; | |||
| } | |||
| } | |||
| draw(ctx, x, y, selected, hover) { | |||
| this.resize(ctx); | |||
| this.left = x - this.width / 2; | |||
| this.top = y - this.height / 2; | |||
| this._drawRawCircle(ctx, x, y, selected, hover, this.options.size); | |||
| this.boundingBox.top = y - this.options.size; | |||
| this.boundingBox.left = x - this.options.size; | |||
| this.boundingBox.right = x + this.options.size; | |||
| this.boundingBox.bottom = y + this.options.size; | |||
| this.labelModule.draw(ctx, x, y, selected); | |||
| } | |||
| distanceToBorder(ctx, angle) { | |||
| this.resize(ctx); | |||
| var a = this.width / 2; | |||
| var b = this.height / 2; | |||
| var w = (Math.sin(angle) * a); | |||
| var h = (Math.cos(angle) * b); | |||
| return a * b / Math.sqrt(w * w + h * h); | |||
| } | |||
| } | |||
| export default Circle; | |||
| @ -0,0 +1,78 @@ | |||
| /** | |||
| * Created by Alex on 3/18/2015. | |||
| */ | |||
| 'use strict'; | |||
| import NodeUtil from './nodeUtil' | |||
| class CircularImage extends NodeUtil { | |||
| constructor (options, body, labelModule, imageObj) { | |||
| super(options, body, labelModule); | |||
| this.imageObj = imageObj; | |||
| } | |||
| setOptions(options) { | |||
| this.options = options; | |||
| } | |||
| resize(ctx) { | |||
| if (this.imageObj.src !== undefined || this.imageObj.width !== undefined || this.imageObj.height !== undefined ) { | |||
| if (!this.width) { | |||
| var diameter = this.options.size * 2; | |||
| this.width = diameter; | |||
| this.height = diameter; | |||
| this._swapToImageResizeWhenImageLoaded = true; | |||
| } | |||
| } | |||
| else { | |||
| if (this._swapToImageResizeWhenImageLoaded) { | |||
| this.width = 0; | |||
| this.height = 0; | |||
| delete this._swapToImageResizeWhenImageLoaded; | |||
| } | |||
| this._resizeImage(ctx); | |||
| } | |||
| } | |||
| draw(ctx, x, y, selected, hover) { | |||
| this.resize(ctx); | |||
| this.left = x - this.width / 2; | |||
| this.top = y - this.height / 2; | |||
| var centerX = this.left + (this.width / 2); | |||
| var centerY = this.top + (this.height / 2); | |||
| var size = Math.abs(this.height / 2); | |||
| this._drawRawCircle(ctx, x, y, selected, hover, size); | |||
| ctx.save(); | |||
| ctx.circle(x, y, size); | |||
| ctx.stroke(); | |||
| ctx.clip(); | |||
| this._drawImageAtPosition(ctx); | |||
| ctx.restore(); | |||
| this.boundingBox.top = y - this.options.size; | |||
| this.boundingBox.left = x - this.options.size; | |||
| this.boundingBox.right = x + this.options.size; | |||
| this.boundingBox.bottom = y + this.options.size; | |||
| this._drawImageLabel(ctx); | |||
| this.boundingBox.left = Math.min(this.boundingBox.left, this.labelModule.size.left); | |||
| this.boundingBox.right = Math.max(this.boundingBox.right, this.labelModule.size.left + this.labelModule.size.width); | |||
| this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelModule.size.height); | |||
| } | |||
| distanceToBorder(ctx, angle) { | |||
| this.resize(ctx); | |||
| return this._distanceToBorder(angle); | |||
| } | |||
| } | |||
| export default CircularImage; | |||
| @ -0,0 +1,68 @@ | |||
| /** | |||
| * Created by Alex on 3/18/2015. | |||
| */ | |||
| 'use strict'; | |||
| class Database { | |||
| constructor (options, body, labelModule) { | |||
| this.body = body; | |||
| this.labelModule = labelModule; | |||
| this.setOptions(options); | |||
| this.top = undefined; | |||
| this.left = undefined; | |||
| this.height = undefined; | |||
| this.height = undefined; | |||
| this.boundingBox = {top: 0, left: 0, right: 0, bottom: 0}; | |||
| } | |||
| setOptions(options) { | |||
| this.options = options; | |||
| } | |||
| resize(ctx, selected) { | |||
| if (this.width === undefined) { | |||
| var margin = 5; | |||
| var textSize = this.labelModule.getTextSize(ctx, selected); | |||
| var size = textSize.width + 2 * margin; | |||
| this.width = size; | |||
| this.height = size; | |||
| } | |||
| } | |||
| draw(ctx, x, y, selected, hover) { | |||
| this.resize(ctx, selected); | |||
| this.left = x - this.width / 2; | |||
| this.top = y - this.height / 2; | |||
| var borderWidth = this.options.borderWidth; | |||
| var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth; | |||
| ctx.strokeStyle = selected ? this.options.color.highlight.border : hover ? this.options.color.hover.border : this.options.color.border; | |||
| ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth); | |||
| ctx.lineWidth *= this.networkScaleInv; | |||
| ctx.lineWidth = Math.min(this.width, ctx.lineWidth); | |||
| ctx.fillStyle = selected ? this.options.color.highlight.background : hover ? this.options.color.hover.background : this.options.color.background; | |||
| ctx.database(x - this.width / 2, y - this.height * 0.5, this.width, this.height); | |||
| ctx.fill(); | |||
| ctx.stroke(); | |||
| this.boundingBox.top = this.top; | |||
| this.boundingBox.left = this.left; | |||
| this.boundingBox.right = this.left + this.width; | |||
| this.boundingBox.bottom = this.top + this.height; | |||
| this.labelModule.draw(ctx, x, y, selected); | |||
| } | |||
| distanceToBorder(ctx, angle) { | |||
| this.resize(ctx); | |||
| var a = this.width / 2; | |||
| var b = this.height / 2; | |||
| var w = (Math.sin(angle) * a); | |||
| var h = (Math.cos(angle) * b); | |||
| return a * b / Math.sqrt(w * w + h * h); | |||
| } | |||
| } | |||
| export default Database; | |||
| @ -0,0 +1,30 @@ | |||
| /** | |||
| * Created by Alex on 3/18/2015. | |||
| */ | |||
| 'use strict'; | |||
| import NodeUtil from './nodeUtil' | |||
| class Dot extends NodeUtil { | |||
| constructor (options, body, labelModule) { | |||
| super(options, body, labelModule) | |||
| } | |||
| setOptions(options) { | |||
| this.options = options; | |||
| } | |||
| resize(ctx) { | |||
| this._resizeShape(); | |||
| } | |||
| draw(ctx, x, y, selected, hover) { | |||
| this._drawShape(ctx, 'circle', 2, x, y, selected, hover); | |||
| } | |||
| distanceToBorder(ctx, angle) { | |||
| return this.options.size + this.options.borderWidth; | |||
| } | |||
| } | |||
| export default Dot; | |||
| @ -1,21 +1,72 @@ | |||
| /** | |||
| * Created by Alex on 3/18/2015. | |||
| */ | |||
| 'use strict'; | |||
| class Ellipse { | |||
| constructor () { | |||
| constructor(options, body, labelModule) { | |||
| this.body = body; | |||
| this.labelModule = labelModule; | |||
| this.setOptions(options); | |||
| this.top = undefined; | |||
| this.left = undefined; | |||
| this.height = undefined; | |||
| this.height = undefined; | |||
| this.boundingBox = {top: 0, left: 0, right: 0, bottom: 0}; | |||
| } | |||
| setOptions(options) { | |||
| this.options = options; | |||
| } | |||
| resize(ctx) { | |||
| resize(ctx, selected) { | |||
| if (this.width === undefined) { | |||
| var textSize = this.labelModule.getTextSize(ctx, selected); | |||
| this.width = textSize.width * 1.5; | |||
| this.height = textSize.height * 2; | |||
| if (this.width < this.height) { | |||
| this.width = this.height; | |||
| } | |||
| } | |||
| } | |||
| draw(ctx) { | |||
| draw(ctx, x, y, selected, hover) { | |||
| this.resize(ctx, selected); | |||
| this.left = x - this.width / 2; | |||
| this.top = y - this.height / 2; | |||
| } | |||
| var borderWidth = this.options.borderWidth; | |||
| var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth; | |||
| ctx.strokeStyle = selected ? this.options.color.highlight.border : hover ? this.options.color.hover.border : this.options.color.border; | |||
| ctx.lineWidth = (selected ? selectionLineWidth : borderWidth); | |||
| ctx.lineWidth /= this.body.view.scale; | |||
| ctx.lineWidth = Math.min(this.width, ctx.lineWidth); | |||
| ctx.fillStyle = selected ? this.options.color.highlight.background : hover ? this.options.color.hover.background : this.options.color.background; | |||
| ctx.ellipse(this.left, this.top, this.width, this.height); | |||
| ctx.fill(); | |||
| ctx.stroke(); | |||
| distanceToBorder() { | |||
| this.boundingBox.left = this.left; | |||
| this.boundingBox.top = this.top; | |||
| this.boundingBox.bottom = this.top + this.height; | |||
| this.boundingBox.right = this.left + this.width; | |||
| this.labelModule.draw(ctx, x, y, selected); | |||
| } | |||
| distanceToBorder(ctx, angle) { | |||
| this.resize(ctx); | |||
| var a = this.width / 2; | |||
| var b = this.height / 2; | |||
| var w = (Math.sin(angle) * a); | |||
| var h = (Math.cos(angle) * b); | |||
| return a * b / Math.sqrt(w * w + h * h); | |||
| } | |||
| } | |||
| } | |||
| export default Ellipse; | |||
| @ -0,0 +1,26 @@ | |||
| /** | |||
| * Created by Alex on 3/18/2015. | |||
| */ | |||
| 'use strict'; | |||
| class Empty { | |||
| constructor (options, labelModule) { | |||
| this.labelModule = labelModule; | |||
| this.setOptions(options); | |||
| this.top = undefined; | |||
| this.left = undefined; | |||
| this.height = undefined; | |||
| this.height = undefined; | |||
| this.boundingBox = {top: 0, left: 0, right: 0, bottom: 0}; | |||
| } | |||
| setOptions() {} | |||
| resize() {} | |||
| draw() {} | |||
| distanceToBorder() {} | |||
| } | |||
| export default Empty; | |||
| @ -0,0 +1,73 @@ | |||
| /** | |||
| * Created by Alex on 3/18/2015. | |||
| */ | |||
| 'use strict'; | |||
| import NodeUtil from './nodeUtil' | |||
| class Icon extends NodeUtil { | |||
| constructor(options, body, labelModule) { | |||
| super(options, body, labelModule); | |||
| } | |||
| setOptions(options) { | |||
| this.options = options; | |||
| } | |||
| resize(ctx) { | |||
| if (this.width === undefined) { | |||
| var margin = 5; | |||
| var iconSize = { | |||
| width: Number(this.options.icon.iconSize), | |||
| height: Number(this.options.icon.iconSize) | |||
| }; | |||
| this.width = iconSize.width + 2 * margin; | |||
| this.height = iconSize.height + 2 * margin; | |||
| } | |||
| } | |||
| draw(ctx, x, y, selected, hover) { | |||
| this.resize(ctx); | |||
| this.options.icon.iconSize = this.options.icon.iconSize || 50; | |||
| this.left = x - this.width * 0.5; | |||
| this.top = y - this.height * 0.5; | |||
| this._icon(ctx, x, y, selected); | |||
| this.boundingBox.top = y - this.options.icon.iconSize * 0.5; | |||
| this.boundingBox.left = x - this.options.icon.iconSize * 0.5; | |||
| this.boundingBox.right = x + this.options.icon.iconSize * 0.5; | |||
| this.boundingBox.bottom = y + this.options.icon.iconSize * 0.5; | |||
| if (this.options.label !== undefined) { | |||
| var iconTextSpacing = 5; | |||
| this.labelModule.draw(ctx, x, y + this.height * 0.5 + iconTextSpacing, selected); | |||
| this.boundingBox.left = Math.min(this.boundingBox.left, this.labelModule.size.left); | |||
| this.boundingBox.right = Math.max(this.boundingBox.right, this.labelModule.size.left + this.labelModule.size.width); | |||
| this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelModule.size.height); | |||
| } | |||
| } | |||
| _icon(ctx, x, y, selected) { | |||
| let iconSize = Number(this.options.icon.iconSize); | |||
| let relativeIconSize = iconSize * this.body.view.scale; | |||
| if (this.options.icon.code && relativeIconSize > this.options.scaling.label.drawThreshold - 1) { | |||
| ctx.font = (selected ? "bold " : "") + iconSize + "px " + this.options.icon.iconFontFace; | |||
| // draw icon | |||
| ctx.fillStyle = this.options.icon.iconColor || "black"; | |||
| ctx.textAlign = "center"; | |||
| ctx.textBaseline = "middle"; | |||
| ctx.fillText(this.options.icon.code, x, y); | |||
| } | |||
| } | |||
| distanceToBorder(ctx, angle) { | |||
| this.resize(ctx); | |||
| this._distanceToBorder(angle); | |||
| } | |||
| } | |||
| export default Icon; | |||
| @ -0,0 +1,70 @@ | |||
| /** | |||
| * Created by Alex on 3/18/2015. | |||
| */ | |||
| 'use strict'; | |||
| import NodeUtil from './nodeUtil' | |||
| class Image extends NodeUtil { | |||
| constructor (options, body, labelModule, imageObj) { | |||
| super(options, body, labelModule); | |||
| this.imageObj = imageObj; | |||
| } | |||
| setOptions(options) { | |||
| this.options = options; | |||
| } | |||
| resize() { | |||
| if (!this.width || !this.height) { // undefined or 0 | |||
| var width, height; | |||
| if (this.value) { | |||
| var scale = this.imageObj.height / this.imageObj.width; | |||
| if (scale !== undefined) { | |||
| width = this.options.size || this.imageObj.width; | |||
| height = this.options.size * scale || this.imageObj.height; | |||
| } | |||
| else { | |||
| width = 0; | |||
| height = 0; | |||
| } | |||
| } | |||
| else { | |||
| width = this.imageObj.width; | |||
| height = this.imageObj.height; | |||
| } | |||
| this.width = width; | |||
| this.height = height; | |||
| } | |||
| } | |||
| draw(ctx, x, y, selected, hover) { | |||
| this.resize(ctx); | |||
| this.left = x - this.width / 2; | |||
| this.top = y - this.height / 2; | |||
| this._drawImageAtPosition(ctx); | |||
| this.boundingBox.top = this.top; | |||
| this.boundingBox.left = this.left; | |||
| this.boundingBox.right = this.left + this.width; | |||
| this.boundingBox.bottom = this.top + this.height; | |||
| this._drawImageLabel(ctx, x, y, selected || hover); | |||
| this.boundingBox.left = Math.min(this.boundingBox.left, this.labelModule.size.left); | |||
| this.boundingBox.right = Math.max(this.boundingBox.right, this.labelModule.size.left + this.labelModule.size.width); | |||
| this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelModule.size.height); | |||
| } | |||
| distanceToBorder(ctx, angle) { | |||
| console.log(this.width) | |||
| this.resize(ctx); | |||
| var a = this.width / 2; | |||
| var b = this.height / 2; | |||
| var w = (Math.sin(angle) * a); | |||
| var h = (Math.cos(angle) * b); | |||
| return a * b / Math.sqrt(w * w + h * h); | |||
| } | |||
| } | |||
| export default Image; | |||
| @ -0,0 +1,126 @@ | |||
| /** | |||
| * Created by Alex on 3/19/2015. | |||
| */ | |||
| class NodeUtil { | |||
| constructor(options, body, labelModule) { | |||
| this.body = body; | |||
| this.labelModule = labelModule; | |||
| this.setOptions(options); | |||
| this.top = undefined; | |||
| this.left = undefined; | |||
| this.height = undefined; | |||
| this.height = undefined; | |||
| this.boundingBox = {top: 0, left: 0, right: 0, bottom: 0}; | |||
| } | |||
| _drawRawCircle(ctx, x, y, selected, hover, size) { | |||
| var borderWidth = this.options.borderWidth; | |||
| var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth; | |||
| ctx.strokeStyle = selected ? this.options.color.highlight.border : hover ? this.options.color.hover.border : this.options.color.border; | |||
| ctx.lineWidth = (selected ? selectionLineWidth : borderWidth); | |||
| ctx.lineWidth *= this.networkScaleInv; | |||
| ctx.lineWidth = Math.min(this.width, ctx.lineWidth); | |||
| ctx.fillStyle = selected ? this.options.color.highlight.background : hover ? this.options.color.hover.background : this.options.color.background; | |||
| ctx.circle(x, y, size); | |||
| ctx.fill(); | |||
| ctx.stroke(); | |||
| } | |||
| _drawImageAtPosition(ctx) { | |||
| if (this.imageObj.width != 0) { | |||
| // draw the image | |||
| ctx.globalAlpha = 1.0; | |||
| ctx.drawImage(this.imageObj, this.left, this.top, this.width, this.height); | |||
| } | |||
| } | |||
| _distanceToBorder(angle) { | |||
| var borderWidth = 1; | |||
| return Math.min( | |||
| Math.abs(this.width / 2 / Math.cos(angle)), | |||
| Math.abs(this.height / 2 / Math.sin(angle))) + borderWidth; | |||
| } | |||
| _resizeShape() { | |||
| if (this.width === undefined) { | |||
| var size = 2 * this.options.size; | |||
| this.width = size; | |||
| this.height = size; | |||
| } | |||
| } | |||
| _drawShape(ctx, shape, sizeMultiplier, x, y, selected, hover) { | |||
| this._resizeShape(); | |||
| this.left = x - this.width / 2; | |||
| this.top = y - this.height / 2; | |||
| var borderWidth = this.options.borderWidth; | |||
| var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth; | |||
| // choose draw method depending on the shape | |||
| switch (shape) { | |||
| case 'dot': | |||
| sizeMultiplier = 2; | |||
| break; | |||
| case 'square': | |||
| sizeMultiplier = 2; | |||
| break; | |||
| case 'triangle': | |||
| sizeMultiplier = 3; | |||
| break; | |||
| case 'triangleDown': | |||
| sizeMultiplier = 3; | |||
| break; | |||
| case 'star': | |||
| sizeMultiplier = 4; | |||
| break; | |||
| } | |||
| ctx.strokeStyle = selected ? this.options.color.highlight.border : hover ? this.options.color.hover.border : this.options.color.border; | |||
| ctx.lineWidth = (selected ? selectionLineWidth : borderWidth); | |||
| ctx.lineWidth /= this.body.view.scale; | |||
| ctx.lineWidth = Math.min(this.width, ctx.lineWidth); | |||
| ctx.fillStyle = selected ? this.options.color.highlight.background : hover ? this.options.color.hover.background : this.options.color.background; | |||
| ctx[shape](x, y, this.options.size); | |||
| ctx.fill(); | |||
| ctx.stroke(); | |||
| this.boundingBox.top = y - this.options.size; | |||
| this.boundingBox.left = x - this.options.size; | |||
| this.boundingBox.right = x + this.options.size; | |||
| this.boundingBox.bottom = y + this.options.size; | |||
| if (this.options.label!== undefined) { | |||
| this.labelModule.draw(ctx, x, y + 0.5* this.height, selected, 'hanging'); | |||
| this.boundingBox.left = Math.min(this.boundingBox.left, this.labelModule.size.left); | |||
| this.boundingBox.right = Math.max(this.boundingBox.right, this.labelModule.size.left + this.labelModule.size.width); | |||
| this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelModule.size.height); | |||
| } | |||
| } | |||
| _drawImageLabel(ctx, x, y, selected) { | |||
| var yLabel; | |||
| var offset = 0; | |||
| if (this.height !== undefined) { | |||
| offset = this.height * 0.5; | |||
| var labelDimensions = this.labelModule.getTextSize(ctx); | |||
| if (labelDimensions.lineCount >= 1) { | |||
| offset += labelDimensions.height / 2; | |||
| offset += 3; | |||
| } | |||
| } | |||
| yLabel = y + offset; | |||
| this.labelModule.draw(ctx, x, yLabel, selected, 'hanging'); | |||
| } | |||
| } | |||
| export default NodeUtil; | |||
| @ -0,0 +1,31 @@ | |||
| /** | |||
| * Created by Alex on 3/18/2015. | |||
| */ | |||
| 'use strict'; | |||
| import NodeUtil from './nodeUtil' | |||
| class Square extends NodeUtil { | |||
| constructor (options, body, labelModule) { | |||
| super(options, body, labelModule) | |||
| } | |||
| setOptions(options) { | |||
| this.options = options; | |||
| } | |||
| resize(ctx) { | |||
| this._resizeShape(); | |||
| } | |||
| draw(ctx, x, y, selected, hover) { | |||
| this._drawShape(ctx, 'square', 2, x, y, selected, hover); | |||
| } | |||
| distanceToBorder(ctx, angle) { | |||
| this.resize(ctx); | |||
| return this._distanceToBorder(angle); | |||
| } | |||
| } | |||
| export default Square; | |||
| @ -0,0 +1,30 @@ | |||
| /** | |||
| * Created by Alex on 3/18/2015. | |||
| */ | |||
| 'use strict'; | |||
| import NodeUtil from './nodeUtil' | |||
| class Star extends NodeUtil { | |||
| constructor (options, body, labelModule) { | |||
| super(options, body, labelModule) | |||
| } | |||
| setOptions(options) { | |||
| this.options = options; | |||
| } | |||
| resize(ctx) { | |||
| this._resizeShape(); | |||
| } | |||
| draw(ctx, x, y, selected, hover) { | |||
| this._drawShape(ctx, 'star', 4, x, y, selected, hover); | |||
| } | |||
| distanceToBorder(ctx, angle) { | |||
| return this._distanceToBorder(angle); | |||
| } | |||
| } | |||
| export default Star; | |||
| @ -0,0 +1,47 @@ | |||
| /** | |||
| * Created by Alex on 3/18/2015. | |||
| */ | |||
| 'use strict'; | |||
| import NodeUtil from './nodeUtil' | |||
| class Text extends NodeUtil { | |||
| constructor (options, body, labelModule) { | |||
| super(options, body, labelModule); | |||
| } | |||
| setOptions(options) { | |||
| this.options = options; | |||
| } | |||
| resize(ctx, selected) { | |||
| if (this.width === undefined) { | |||
| var margin = 5; | |||
| var textSize = this.labelModule.getTextSize(ctx,selected); | |||
| this.width = textSize.width + 2 * margin; | |||
| this.height = textSize.height + 2 * margin; | |||
| } | |||
| } | |||
| draw(ctx, x, y, selected, hover) { | |||
| this.resize(ctx, selected || hover); | |||
| this.left = x - this.width / 2; | |||
| this.top = y - this.height / 2; | |||
| this.labelModule.draw(ctx, x, y, selected || hover); | |||
| this.boundingBox.top = this.top; | |||
| this.boundingBox.left = this.left; | |||
| this.boundingBox.right = this.left + this.width; | |||
| this.boundingBox.bottom = this.top + this.height; | |||
| } | |||
| distanceToBorder(ctx, angle) { | |||
| console.log("hererer") | |||
| console.log(this._distanceToBorder(angle)) | |||
| this.resize(ctx); | |||
| return this._distanceToBorder(angle); | |||
| } | |||
| } | |||
| export default Text; | |||
| @ -0,0 +1,30 @@ | |||
| /** | |||
| * Created by Alex on 3/18/2015. | |||
| */ | |||
| 'use strict'; | |||
| import NodeUtil from './nodeUtil' | |||
| class Triangle extends NodeUtil { | |||
| constructor (options, body, labelModule) { | |||
| super(options, body, labelModule) | |||
| } | |||
| setOptions(options) { | |||
| this.options = options; | |||
| } | |||
| resize(ctx) { | |||
| this._resizeShape(); | |||
| } | |||
| draw(ctx, x, y, selected, hover) { | |||
| this._drawShape(ctx, 'triangle', 3, x, y, selected, hover); | |||
| } | |||
| distanceToBorder(ctx, angle) { | |||
| return this._distanceToBorder(angle); | |||
| } | |||
| } | |||
| export default Triangle; | |||
| @ -0,0 +1,30 @@ | |||
| /** | |||
| * Created by Alex on 3/18/2015. | |||
| */ | |||
| 'use strict'; | |||
| import NodeUtil from './nodeUtil' | |||
| class TriangleDown extends NodeUtil { | |||
| constructor (options, body, labelModule) { | |||
| super(options, body, labelModule) | |||
| } | |||
| setOptions(options) { | |||
| this.options = options; | |||
| } | |||
| resize(ctx) { | |||
| this._resizeShape(); | |||
| } | |||
| draw(ctx, x, y, selected, hover) { | |||
| this._drawShape(ctx, 'triangleDown', 3, x, y, selected, hover); | |||
| } | |||
| distanceToBorder(ctx, angle) { | |||
| return this._distanceToBorder(angle); | |||
| } | |||
| } | |||
| export default TriangleDown; | |||