Browse Source

seperated all node styles from the main node object

flowchartTest
Alex de Mulder 10 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"> <script type="text/javascript">
// create an array with nodes // create an array with nodes
var 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 // 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 Emitter = require('emitter-component');
var Hammer = require('../module/hammer'); var Hammer = require('../module/hammer');
var util = require('../util'); var util = require('../util');
@ -11,8 +14,7 @@ var Popup = require('./Popup');
var Activator = require('../shared/Activator'); var Activator = require('../shared/Activator');
var locales = require('./locales'); var locales = require('./locales');
// Load custom shapes into CanvasRenderingContext2D
require('./shapes');
import NodesHandler from './modules/NodesHandler'; import NodesHandler from './modules/NodesHandler';
import EdgesHandler from './modules/EdgesHandler'; 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 ('clickToUse' in options) {
if (options.clickToUse === true) { if (options.clickToUse === true) {

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

@ -6,7 +6,7 @@
var util = require("../../util"); var util = require("../../util");
var DataSet = require('../../DataSet'); var DataSet = require('../../DataSet');
var DataView = require('../../DataView'); var DataView = require('../../DataView');
var Edge = require("./components/edges/EdgeMain");
var Edge = require("./components/Edge");
class EdgesHandler { class EdgesHandler {
constructor(body, images, groups) { 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 DataSet = require('../../DataSet');
var DataView = require('../../DataView'); var DataView = require('../../DataView');
import Node from "./components/nodes/NodeMain";
import Node from "./components/Node";
class NodesHandler { class NodesHandler {
constructor(body, images, groups, layoutEngine) { 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. * Created by Alex on 2/27/2015.
*/ */
var Node = require("./components/nodes/NodeMain");
var Node = require("./components/Node");
var util = require('../../util'); var util = require('../../util');
class SelectionHandler { class SelectionHandler {
@ -112,11 +112,10 @@ class SelectionHandler {
_getAllNodesOverlappingWith(object) { _getAllNodesOverlappingWith(object) {
var overlappingNodes = []; var overlappingNodes = [];
var nodes = this.body.nodes; 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; return overlappingNodes;
@ -172,11 +171,10 @@ class SelectionHandler {
*/ */
_getEdgesOverlappingWith(object, overlappingEdges) { _getEdgesOverlappingWith(object, overlappingEdges) {
var edges = this.body.edges; 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 * @class Edge
* *
@ -74,6 +74,7 @@ class Edge {
this.colorDirty = true; this.colorDirty = true;
var fields = [ var fields = [
'id',
'font', 'font',
'hidden', 'hidden',
'hoverWidth', 'hoverWidth',
@ -316,7 +317,7 @@ class Edge {
ctx.save(); ctx.save();
// if the label has to be rotated: // 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); this.labelModule.calculateLabelSize(ctx,selected,point.x,point.y);
ctx.translate(point.x, this.labelModule.size.yLine); ctx.translate(point.x, this.labelModule.size.yLine);
this._rotateForLabelAlignment(ctx); this._rotateForLabelAlignment(ctx);
@ -1210,8 +1211,6 @@ class Edge {
this.networkScaleInv = 1.0 / scale; this.networkScaleInv = 1.0 / scale;
} }
select() { select() {
this.selected = true; 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. * Created by Alex on 3/18/2015.
*/ */
'use strict';
class Ellipse { 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 * @param {CanvasRenderingContext2D} ctx
* @private * @private
*/ */
_drawLabelRect(ctx) {
_drawBackground(ctx) {
if (this.options.font.background !== undefined && this.options.font.background !== "none") { if (this.options.font.background !== undefined && this.options.font.background !== "none") {
ctx.fillStyle = this.options.font.background; 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 * @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.fillStyle = fontColor;
ctx.textAlign = 'center'; 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) // check for label alignment (for edges)
// TODO: make alignment for nodes // TODO: make alignment for nodes
if (this.options.font.align !== 'horizontal') { if (this.options.font.align !== 'horizontal') {
@ -168,19 +141,95 @@ class Label {
ctx.textBaseline = baseline; 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 * @date 2012-08-08
*/ */
CanvasRenderingContext2D.prototype.dashedLine = function(x,y,x2,y2,dashArray){ 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; var dashCount = dashArray.length;
this.moveTo(x, y); 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; 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; x += xStep;
y += slope*xStep;
this[draw ? 'lineTo' : 'moveTo'](x,y);
y += slope * xStep;
this[draw ? 'lineTo' : 'moveTo'](x, y);
distRemaining -= dashLength; distRemaining -= dashLength;
draw = !draw; draw = !draw;
} }

Loading…
Cancel
Save