Browse Source

seperated all node styles from the main node object

flowchartTest
Alex de Mulder 9 years ago
parent
commit
b2178ffcc9
26 changed files with 3234 additions and 2042 deletions
  1. +1789
    -968
      dist/vis.js
  2. +5
    -5
      examples/network/01_basic_usage.html
  3. +59
    -57
      lib/network/Network.js
  4. +1
    -1
      lib/network/modules/EdgesHandler.js
  5. +1
    -1
      lib/network/modules/NodesHandler.js
  6. +9
    -11
      lib/network/modules/SelectionHandler.js
  7. +4
    -5
      lib/network/modules/components/Edge.js
  8. +386
    -0
      lib/network/modules/components/Node.js
  9. +0
    -881
      lib/network/modules/components/nodes/NodeMain.js
  10. +68
    -0
      lib/network/modules/components/nodes/box.js
  11. +60
    -0
      lib/network/modules/components/nodes/circle.js
  12. +78
    -0
      lib/network/modules/components/nodes/circularImage.js
  13. +68
    -0
      lib/network/modules/components/nodes/database.js
  14. +30
    -0
      lib/network/modules/components/nodes/dot.js
  15. +57
    -6
      lib/network/modules/components/nodes/ellipse.js
  16. +26
    -0
      lib/network/modules/components/nodes/empty.js
  17. +73
    -0
      lib/network/modules/components/nodes/icon.js
  18. +70
    -0
      lib/network/modules/components/nodes/image.js
  19. +126
    -0
      lib/network/modules/components/nodes/nodeUtil.js
  20. +31
    -0
      lib/network/modules/components/nodes/square.js
  21. +30
    -0
      lib/network/modules/components/nodes/star.js
  22. +47
    -0
      lib/network/modules/components/nodes/text.js
  23. +30
    -0
      lib/network/modules/components/nodes/triangle.js
  24. +30
    -0
      lib/network/modules/components/nodes/triangleDown.js
  25. +144
    -95
      lib/network/modules/components/unified/label.js
  26. +12
    -12
      lib/network/shapes.js

+ 1789
- 968
dist/vis.js
File diff suppressed because it is too large
View File


+ 5
- 5
examples/network/01_basic_usage.html View File

@ -22,11 +22,11 @@
<script type="text/javascript">
// create an array with nodes
var nodes = [
{id: 1, label: 'Node 1'},
{id: 2, label: 'Node 2', shape:'dot'},
{id: 3, label: 'Node 3'},
{id: 4, label: 'Node 4'},
{id: 5, label: 'Node 5'}
{id: 1, label: 'Node 1', shape:'text'},
{id: 2, label: 'Node 2', shape:'square'},
{id: 3, label: 'Node 3', shape:'triangle'},
{id: 4, label: 'Node 4', shape:'star'},
{id: 5, label: 'Node 5', shape:'triangleDown'}
];
// create an array with edges

+ 59
- 57
lib/network/Network.js View File

@ -1,3 +1,6 @@
// Load custom shapes into CanvasRenderingContext2D
require('./shapes');
var Emitter = require('emitter-component');
var Hammer = require('../module/hammer');
var util = require('../util');
@ -11,8 +14,7 @@ var Popup = require('./Popup');
var Activator = require('../shared/Activator');
var locales = require('./locales');
// Load custom shapes into CanvasRenderingContext2D
require('./shapes');
import NodesHandler from './modules/NodesHandler';
import EdgesHandler from './modules/EdgesHandler';
@ -292,61 +294,61 @@ Network.prototype.setOptions = function (options) {
//}
// TODO: work out these options and document them
if (options.edges) {
if (options.edges.color !== undefined) {
if (util.isString(options.edges.color)) {
this.constants.edges.color = {};
this.constants.edges.color.color = options.edges.color;
this.constants.edges.color.highlight = options.edges.color;
this.constants.edges.color.hover = options.edges.color;
}
else {
if (options.edges.color.color !== undefined) {this.constants.edges.color.color = options.edges.color.color;}
if (options.edges.color.highlight !== undefined) {this.constants.edges.color.highlight = options.edges.color.highlight;}
if (options.edges.color.hover !== undefined) {this.constants.edges.color.hover = options.edges.color.hover;}
}
this.constants.edges.inheritColor = false;
}
if (!options.edges.fontColor) {
if (options.edges.color !== undefined) {
if (util.isString(options.edges.color)) {this.constants.edges.fontColor = options.edges.color;}
else if (options.edges.color.color !== undefined) {this.constants.edges.fontColor = options.edges.color.color;}
}
}
}
if (options.nodes) {
if (options.nodes.color) {
var newColorObj = util.parseColor(options.nodes.color);
this.constants.nodes.color.background = newColorObj.background;
this.constants.nodes.color.border = newColorObj.border;
this.constants.nodes.color.highlight.background = newColorObj.highlight.background;
this.constants.nodes.color.highlight.border = newColorObj.highlight.border;
this.constants.nodes.color.hover.background = newColorObj.hover.background;
this.constants.nodes.color.hover.border = newColorObj.hover.border;
}
}
if (options.groups) {
for (var groupname in options.groups) {
if (options.groups.hasOwnProperty(groupname)) {
var group = options.groups[groupname];
this.groups.add(groupname, group);
}
}
}
if (options.tooltip) {
for (prop in options.tooltip) {
if (options.tooltip.hasOwnProperty(prop)) {
this.constants.tooltip[prop] = options.tooltip[prop];
}
}
if (options.tooltip.color) {
this.constants.tooltip.color = util.parseColor(options.tooltip.color);
}
}
//// TODO: work out these options and document them
//if (options.edges) {
// if (options.edges.color !== undefined) {
// if (util.isString(options.edges.color)) {
// this.constants.edges.color = {};
// this.constants.edges.color.color = options.edges.color;
// this.constants.edges.color.highlight = options.edges.color;
// this.constants.edges.color.hover = options.edges.color;
// }
// else {
// if (options.edges.color.color !== undefined) {this.constants.edges.color.color = options.edges.color.color;}
// if (options.edges.color.highlight !== undefined) {this.constants.edges.color.highlight = options.edges.color.highlight;}
// if (options.edges.color.hover !== undefined) {this.constants.edges.color.hover = options.edges.color.hover;}
// }
// this.constants.edges.inheritColor = false;
// }
//
// if (!options.edges.fontColor) {
// if (options.edges.color !== undefined) {
// if (util.isString(options.edges.color)) {this.constants.edges.fontColor = options.edges.color;}
// else if (options.edges.color.color !== undefined) {this.constants.edges.fontColor = options.edges.color.color;}
// }
// }
//}
//
//if (options.nodes) {
// if (options.nodes.color) {
// var newColorObj = util.parseColor(options.nodes.color);
// this.constants.nodes.color.background = newColorObj.background;
// this.constants.nodes.color.border = newColorObj.border;
// this.constants.nodes.color.highlight.background = newColorObj.highlight.background;
// this.constants.nodes.color.highlight.border = newColorObj.highlight.border;
// this.constants.nodes.color.hover.background = newColorObj.hover.background;
// this.constants.nodes.color.hover.border = newColorObj.hover.border;
// }
//}
//if (options.groups) {
// for (var groupname in options.groups) {
// if (options.groups.hasOwnProperty(groupname)) {
// var group = options.groups[groupname];
// this.groups.add(groupname, group);
// }
// }
//}
//
//if (options.tooltip) {
// for (prop in options.tooltip) {
// if (options.tooltip.hasOwnProperty(prop)) {
// this.constants.tooltip[prop] = options.tooltip[prop];
// }
// }
// if (options.tooltip.color) {
// this.constants.tooltip.color = util.parseColor(options.tooltip.color);
// }
//}
if ('clickToUse' in options) {
if (options.clickToUse === true) {

+ 1
- 1
lib/network/modules/EdgesHandler.js View File

@ -6,7 +6,7 @@
var util = require("../../util");
var DataSet = require('../../DataSet');
var DataView = require('../../DataView');
var Edge = require("./components/edges/EdgeMain");
var Edge = require("./components/Edge");
class EdgesHandler {
constructor(body, images, groups) {

+ 1
- 1
lib/network/modules/NodesHandler.js View File

@ -6,7 +6,7 @@ var util = require("../../util");
var DataSet = require('../../DataSet');
var DataView = require('../../DataView');
import Node from "./components/nodes/NodeMain";
import Node from "./components/Node";
class NodesHandler {
constructor(body, images, groups, layoutEngine) {

+ 9
- 11
lib/network/modules/SelectionHandler.js View File

@ -2,7 +2,7 @@
* Created by Alex on 2/27/2015.
*/
var Node = require("./components/nodes/NodeMain");
var Node = require("./components/Node");
var util = require('../../util');
class SelectionHandler {
@ -112,11 +112,10 @@ class SelectionHandler {
_getAllNodesOverlappingWith(object) {
var overlappingNodes = [];
var nodes = this.body.nodes;
for (var nodeId in nodes) {
if (nodes.hasOwnProperty(nodeId)) {
if (nodes[nodeId].isOverlappingWith(object)) {
overlappingNodes.push(nodeId);
}
for (let i = 0; i < this.body.nodeIndices.length; i++) {
let nodeId = this.body.nodeIndices[i];
if (nodes[nodeId].isOverlappingWith(object)) {
overlappingNodes.push(nodeId);
}
}
return overlappingNodes;
@ -172,11 +171,10 @@ class SelectionHandler {
*/
_getEdgesOverlappingWith(object, overlappingEdges) {
var edges = this.body.edges;
for (var edgeId in edges) {
if (edges.hasOwnProperty(edgeId)) {
if (edges[edgeId].isOverlappingWith(object)) {
overlappingEdges.push(edgeId);
}
for (let i = 0; i < this.body.edgeIndices.length; i++) {
let edgeId = this.body.edgeIndices[i];
if (edges[edgeId].isOverlappingWith(object)) {
overlappingEdges.push(edgeId);
}
}
}

lib/network/modules/components/edges/EdgeMain.js → lib/network/modules/components/Edge.js View File

@ -1,7 +1,7 @@
var util = require('../../../../util');
var util = require('../../../util');
import Label from '../unified/label.js'
import Label from './unified/label.js'
/**
* @class Edge
*
@ -74,6 +74,7 @@ class Edge {
this.colorDirty = true;
var fields = [
'id',
'font',
'hidden',
'hoverWidth',
@ -316,7 +317,7 @@ class Edge {
ctx.save();
// if the label has to be rotated:
if (this.options.font.align != "horizontal") {
if (this.options.font.align !== "horizontal") {
this.labelModule.calculateLabelSize(ctx,selected,point.x,point.y);
ctx.translate(point.x, this.labelModule.size.yLine);
this._rotateForLabelAlignment(ctx);
@ -1210,8 +1211,6 @@ class Edge {
this.networkScaleInv = 1.0 / scale;
}
select() {
this.selected = true;
}

+ 386
- 0
lib/network/modules/components/Node.js View File

@ -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;

+ 0
- 881
lib/network/modules/components/nodes/NodeMain.js View File

@ -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;

+ 68
- 0
lib/network/modules/components/nodes/box.js View File

@ -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;

+ 60
- 0
lib/network/modules/components/nodes/circle.js View File

@ -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;

+ 78
- 0
lib/network/modules/components/nodes/circularImage.js View File

@ -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;

+ 68
- 0
lib/network/modules/components/nodes/database.js View File

@ -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;

+ 30
- 0
lib/network/modules/components/nodes/dot.js View File

@ -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;

+ 57
- 6
lib/network/modules/components/nodes/ellipse.js View File

@ -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;

+ 26
- 0
lib/network/modules/components/nodes/empty.js View File

@ -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;

+ 73
- 0
lib/network/modules/components/nodes/icon.js View File

@ -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;

+ 70
- 0
lib/network/modules/components/nodes/image.js View File

@ -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;

+ 126
- 0
lib/network/modules/components/nodes/nodeUtil.js View File

@ -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;

+ 31
- 0
lib/network/modules/components/nodes/square.js View File

@ -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;

+ 30
- 0
lib/network/modules/components/nodes/star.js View File

@ -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;

+ 47
- 0
lib/network/modules/components/nodes/text.js View File

@ -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;

+ 30
- 0
lib/network/modules/components/nodes/triangle.js View File

@ -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;

+ 30
- 0
lib/network/modules/components/nodes/triangleDown.js View File

@ -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;

+ 144
- 95
lib/network/modules/components/unified/label.js View File

@ -19,85 +19,40 @@ class Label {
}
}
draw(ctx, x, y, selected, baseline = 'middle') {
if (this.options.label !== undefined) {
// check if we have to render the label
let relativeFontSize = Number(this.options.font.size) * this.body.view.scale;
if (this.options.label && relativeFontSize >= this.options.scaling.label.drawThreshold - 1) {
// this ensures that there will not be HUGE letters on screen by setting an upper limit on the visible text size (regardless of zoomLevel)
let fontSize = Number(this.options.font.size);
if (relativeFontSize >= this.options.scaling.label.maxVisible) {
fontSize = Number(this.options.scaling.label.maxVisible) / this.body.view.scale;
}
// notify the canvas of the fontsize and thickness
ctx.font = (selected ? "bold " : "") + fontSize + "px " + this.options.font.face;
// update the size cache if required
if (this.labelDirty == true) {
this.calculateLabelSize(ctx, selected, x, y, baseline);
}
// create some of the local variables
let yLine = this.size.yLine;
let lines = String(this.options.label).split('\n');
let lineCount = lines.length;
// create the fontfill background
this._drawLabelRect(ctx);
// draw text
this._drawLabelText(ctx, x, yLine, lines, lineCount, fontSize, baseline, relativeFontSize);
}
}
}
/**
* Main function. This is called from anything that wants to draw a label.
* @param ctx
* @param x
* @param y
* @param selected
* @param baseline
*/
draw(ctx, x, y, selected, baseline = 'middle') {
// if no label, return
if (this.options.label === undefined)
return;
getTextSize(ctx, selected) {
if (this.options.label !== undefined) {
this._calculateLabelSize(ctx,selected);
}
else {
this.size = {top: 0, left: 0, width: 0, height: 0, yLine: 0};
}
return this.size;
}
_calculateLabelSize(ctx,selected,x,y,baseline) {
ctx.font = (selected ? "bold " : "") + this.options.font.size + "px " + this.options.font.face;
let lines = String(this.options.label).split('\n');
let lineCount = lines.length;
let yLine = y + (1 - lineCount) * 0.5 * this.options.font.size;
// check if we have to render the label
let viewFontSize = Number(this.options.font.size) * this.body.view.scale;
if (this.options.label && viewFontSize < this.options.scaling.label.drawThreshold - 1)
return;
let width = ctx.measureText(lines[0]).width;
for (let i = 1; i < lineCount; i++) {
let lineWidth = ctx.measureText(lines[i]).width;
width = lineWidth > width ? lineWidth : width;
}
let height = this.options.font.size * lineCount;
let left = x - width * 0.5;
let top = y - height * 0.5;
if (baseline == "hanging") {
top += 0.5 * this.options.font.size;
top += 4; // distance from node, required because we use hanging. Hanging has less difference between browsers
yLine += 4; // distance from node
}
// update the size cache if required
this.calculateLabelSize(ctx, selected, x, y, baseline);
// cache
this.size = {top: top, left: left, width: width, height: height, yLine: yLine};
// create the fontfill background
this._drawBackground(ctx);
// draw text
this._drawText(ctx, selected, x, y, baseline);
}
calculateLabelSize(ctx,selected,x=0,y=0,baseline='middle') {
if (this.labelDirty == true) {
this._calculateLabelSize(ctx, selected, x, y, baseline);
}
}
/**
* Draws the label rectangle
* Draws the label background
* @param {CanvasRenderingContext2D} ctx
* @private
*/
_drawLabelRect(ctx) {
_drawBackground(ctx) {
if (this.options.font.background !== undefined && this.options.font.background !== "none") {
ctx.fillStyle = this.options.font.background;
@ -122,29 +77,47 @@ class Label {
/**
* Draws the label text
* @param {CanvasRenderingContext2D} ctx
* @param {Number} x
* @param {Number} yLine
* @param {Array} lines
* @param {Number} lineCount
* @param {Number} fontSize
*
* @param ctx
* @param x
* @param baseline
* @private
*/
_drawLabelText(ctx, x, yLine, lines, lineCount, fontSize, baseline = 'middle', relativeFontSize = this.options.font.size) {
// fade in when relative scale is between threshold and threshold - 1
let fontColor = this.options.font.color || "#000000";
let strokeColor = this.options.font.strokeColor;
if (relativeFontSize <= this.options.scaling.label.drawThreshold) {
let opacity = Math.max(0, Math.min(1, 1 - (this.options.scaling.label.drawThreshold - relativeFontSize)));
fontColor = util.overrideOpacity(fontColor, opacity);
strokeColor = util.overrideOpacity(strokeColor, opacity);
_drawText(ctx, selected, x, y, baseline = 'middle') {
let fontSize = Number(this.options.font.size);
let viewFontSize = fontSize * this.body.view.scale;
// this ensures that there will not be HUGE letters on screen by setting an upper limit on the visible text size (regardless of zoomLevel)
if (viewFontSize >= this.options.scaling.label.maxVisible) {
fontSize = Number(this.options.scaling.label.maxVisible) / this.body.view.scale;
}
// draw text
let yLine = this.size.yLine;
let [fontColor, strokeColor] = this._getColor(viewFontSize);
[x, yLine] = this._setAlignment(ctx, x, yLine, baseline);
// configure context for drawing the text
ctx.font = (selected ? 'bold ' : '') + fontSize + "px " + this.options.font.face;
ctx.fillStyle = fontColor;
ctx.textAlign = 'center';
// set the strokeWidth
if (this.options.font.stroke > 0) {
ctx.lineWidth = this.options.font.stroke;
ctx.strokeStyle = strokeColor;
ctx.lineJoin = 'round';
}
// draw the text
for (let i = 0; i < this.lineCount; i++) {
if (this.options.font.stroke > 0) {
ctx.strokeText(this.lines[i], x, yLine);
}
ctx.fillText(this.lines[i], x, yLine);
yLine += fontSize;
}
}
_setAlignment(ctx, x, yLine, baseline) {
// check for label alignment (for edges)
// TODO: make alignment for nodes
if (this.options.font.align !== 'horizontal') {
@ -168,19 +141,95 @@ class Label {
ctx.textBaseline = baseline;
}
// check for strokeWidth
if (this.options.font.stroke > 0) {
ctx.lineWidth = this.options.font.stroke;
ctx.strokeStyle = strokeColor;
ctx.lineJoin = 'round';
return [x,yLine];
}
/**
* fade in when relative scale is between threshold and threshold - 1.
* If the relative scale would be smaller than threshold -1 the draw function would have returned before coming here.
*
* @param viewFontSize
* @returns {*[]}
* @private
*/
_getColor(viewFontSize) {
let fontColor = this.options.font.color || '#000000';
let strokeColor = this.options.font.strokeColor || '#ffffff';
if (viewFontSize <= this.options.scaling.label.drawThreshold) {
let opacity = Math.max(0, Math.min(1, 1 - (this.options.scaling.label.drawThreshold - viewFontSize)));
fontColor = util.overrideOpacity(fontColor, opacity);
strokeColor = util.overrideOpacity(strokeColor, opacity);
}
for (let i = 0; i < lineCount; i++) {
if (this.options.font.stroke > 0) {
ctx.strokeText(lines[i], x, yLine);
return [fontColor, strokeColor];
}
/**
*
* @param ctx
* @param selected
* @returns {{width: number, height: number}}
*/
getTextSize(ctx, selected = false) {
let size = {
width: this._processLabel(ctx,selected),
height: this.options.font.size * this.lineCount
};
return size;
}
/**
*
* @param ctx
* @param selected
* @param x
* @param y
* @param baseline
*/
calculateLabelSize(ctx, selected, x = 0, y = 0, baseline = 'middle') {
if (this.labelDirty === true) {
this.size.width = this._processLabel(ctx,selected);
}
this.size.height = this.options.font.size * this.lineCount;
this.size.left = x - this.size.width * 0.5;
this.size.top = y - this.size.height * 0.5;
this.size.yLine = y + (1 - this.lineCount) * 0.5 * this.options.font.size;
if (baseline == "hanging") {
this.size.top += 0.5 * this.options.font.size;
this.size.top += 4; // distance from node, required because we use hanging. Hanging has less difference between browsers
this.size.yLine += 4; // distance from node
}
this.labelDirty = false;
}
/**
* This calculates the width as well as explodes the label string and calculates the amount of lines.
* @param ctx
* @param selected
* @returns {number}
* @private
*/
_processLabel(ctx,selected) {
let width = 0;
let lines = [''];
let lineCount = 0;
if (this.options.label !== undefined) {
lines = String(this.options.label).split('\n');
lineCount = lines.length;
ctx.font = (selected ? 'bold ' : '') + this.options.font.size + "px " + this.options.font.face;
width = ctx.measureText(lines[0]).width;
for (let i = 1; i < lineCount; i++) {
let lineWidth = ctx.measureText(lines[i]).width;
width = lineWidth > width ? lineWidth : width;
}
ctx.fillText(lines[i], x, yLine);
yLine += fontSize;
}
this.lines = lines;
this.lineCount = lineCount;
return width;
}
}

+ 12
- 12
lib/network/shapes.js View File

@ -200,22 +200,22 @@ if (typeof CanvasRenderingContext2D !== 'undefined') {
* @date 2012-08-08
*/
CanvasRenderingContext2D.prototype.dashedLine = function(x,y,x2,y2,dashArray){
if (!dashArray) dashArray=[10,5];
if (dashLength==0) dashLength = 0.001; // Hack for Safari
if (!dashArray) dashArray = [10, 5];
if (dashLength == 0) dashLength = 0.001; // Hack for Safari
var dashCount = dashArray.length;
this.moveTo(x, y);
var dx = (x2-x), dy = (y2-y);
var slope = dy/dx;
var distRemaining = Math.sqrt( dx*dx + dy*dy );
var dashIndex=0, draw=true;
while (distRemaining>=0.1){
var dashLength = dashArray[dashIndex++%dashCount];
var dx = (x2 - x), dy = (y2 - y);
var slope = dy / dx;
var distRemaining = Math.sqrt(dx * dx + dy * dy);
var dashIndex = 0, draw = true;
while (distRemaining >= 0.1) {
var dashLength = dashArray[dashIndex++ % dashCount];
if (dashLength > distRemaining) dashLength = distRemaining;
var xStep = Math.sqrt( dashLength*dashLength / (1 + slope*slope) );
if (dx<0) xStep = -xStep;
var xStep = Math.sqrt(dashLength * dashLength / (1 + slope * slope));
if (dx < 0) xStep = -xStep;
x += xStep;
y += slope*xStep;
this[draw ? 'lineTo' : 'moveTo'](x,y);
y += slope * xStep;
this[draw ? 'lineTo' : 'moveTo'](x, y);
distRemaining -= dashLength;
draw = !draw;
}

Loading…
Cancel
Save