Browse Source

fantastic clustering in network woohoo, added point labels for bargraphs in graph2d

flowchartTest
Alex de Mulder 9 years ago
parent
commit
a9b7485b86
18 changed files with 27297 additions and 28601 deletions
  1. +26689
    -27401
      dist/vis.js
  2. +1
    -1
      dist/vis.map
  3. +14
    -14
      dist/vis.min.js
  4. +1
    -0
      examples/graph2d/19_labels.html
  5. +102
    -0
      examples/network/39_newClustering.html
  6. +10
    -7
      lib/network/Edge.js
  7. +41
    -35
      lib/network/Network.js
  8. +11
    -156
      lib/network/Node.js
  9. +392
    -961
      lib/network/mixins/ClusterMixin.js
  10. +6
    -6
      lib/network/mixins/SelectionMixin.js
  11. +1
    -1
      lib/network/mixins/physics/HierarchialRepulsionMixin.js
  12. +3
    -14
      lib/network/mixins/physics/PhysicsMixin.js
  13. +17
    -0
      lib/network/modules/ClusterEngine.js
  14. +0
    -0
      lib/network/modules/clustering/backend.js
  15. +0
    -0
      lib/network/modules/clustering/public.js
  16. +0
    -0
      lib/network/modules/clustering/support.js
  17. +4
    -2
      lib/timeline/component/graph2d_types/bar.js
  18. +5
    -3
      lib/util.js

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


+ 1
- 1
dist/vis.map
File diff suppressed because it is too large
View File


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


+ 1
- 0
examples/graph2d/19_labels.html View File

@ -57,6 +57,7 @@
var options = { var options = {
start: '2014-06-10', start: '2014-06-10',
end: '2014-06-18', end: '2014-06-18',
style:'bar'
}; };
var graph2d = new vis.Graph2d(container, dataset, options); var graph2d = new vis.Graph2d(container, dataset, options);
</script> </script>

+ 102
- 0
examples/network/39_newClustering.html View File

@ -0,0 +1,102 @@
<!doctype html>
<html>
<head>
<title>Network | Basic usage</title>
<script type="text/javascript" src="../../dist/vis.js"></script>
<link href="../../dist/vis.css" rel="stylesheet" type="text/css" />
<style type="text/css">
#mynetwork {
width: 400px;
height: 400px;
border: 1px solid lightgray;
}
</style>
</head>
<body>
<div id="mynetwork"></div>
<script type="text/javascript">
// create an array with nodes
var nodes = [
{id: 1, label: 'Node 1'},
{id: 2, label: 'Node 2'},
{id: 3, label: 'Node 3'},
{id: 4, label: 'Node 4'},
{id: 5, label: 'Node 5'},
{id: 6, label: 'Node 6', cid:1},
{id: 7, label: 'Node 7', cid:1},
{id: 8, label: 'Node 8', cid:1},
{id: 9, label: 'Node 9', cid:1},
{id: 10, label: 'Node 10', cid:1}
];
// create an array with edges
var edges = [
{from: 1, to: 2},
{from: 1, to: 3},
{from: 10, to: 4},
{from: 2, to: 5},
{from: 6, to: 2},
{from: 7, to: 5},
{from: 8, to: 6},
{from: 9, to: 7},
{from: 10, to: 9}
];
// create a network
var container = document.getElementById('mynetwork');
var data = {
nodes: nodes,
edges: edges
};
var options = {clustering:true};
var network = new vis.Network(container, data, options);
var clusterOptions = {
joinCondition:function(parentOptions,childOptions) {
return true;
},
processClusterProperties: function (properties, childNodes, childEdges) {
return properties;
},
clusterNodeProperties: {id:'bla', borderWidth:8},
}
var clusterOptionsByData = {
joinCondition:function(childOptions) {
return childOptions.cid == 1;
},
processClusterProperties: function (properties, childNodes, childEdges) {
return properties;
},
clusterNodeProperties: {id:'bla', borderWidth:8},
}
// network.clusterByNodeData(clusterOptionsByData)
network.clusterOutliers({clusterNodeProperties: {borderWidth:8}})
// network.clusterByConnection(2, clusterOptions);
// network.clusterByConnection(9, {
// joinCondition:function(parentOptions,childOptions) {return true;},
// processProperties:function (properties, childNodes, childEdges) {
// return properties;
// },
// clusterNodeProperties: {id:'bla2', label:"bla2", borderWidth:8}
// });
network.on("select", function(params) {
if (params.nodes.length == 1) {
if (params.nodes[0].indexOf("cluster") != -1) {
network.openCluster(params.nodes[0])
}
}
})
// network.openCluster('bla');
// network.openCluster('bla2');
</script>
</body>
</html>

+ 10
- 7
lib/network/Edge.js View File

@ -23,6 +23,7 @@ function Edge (properties, network, networkConstants) {
var fields = ['edges','physics']; var fields = ['edges','physics'];
var constants = util.selectiveBridgeObject(fields,networkConstants); var constants = util.selectiveBridgeObject(fields,networkConstants);
this.options = constants.edges; this.options = constants.edges;
this.physics = constants.physics; this.physics = constants.physics;
this.options['smoothCurves'] = networkConstants['smoothCurves']; this.options['smoothCurves'] = networkConstants['smoothCurves'];
@ -46,13 +47,13 @@ function Edge (properties, network, networkConstants) {
this.to = null; // a node this.to = null; // a node
this.via = null; // a temp node this.via = null; // a temp node
this.fromBackup = null; // used to clean up after reconnect
this.toBackup = null;; // used to clean up after reconnect
this.fromBackup = null; // used to clean up after reconnect (used for manipulation)
this.toBackup = null; // used to clean up after reconnect (used for manipulation)
// we use this to be able to reconnect the edge to a cluster if its node is put into a cluster // we use this to be able to reconnect the edge to a cluster if its node is put into a cluster
// by storing the original information we can revert to the original connection when the cluser is opened. // by storing the original information we can revert to the original connection when the cluser is opened.
this.originalFromId = [];
this.originalToId = [];
this.fromArray = [];
this.toArray = [];
this.connected = false; this.connected = false;
@ -76,10 +77,11 @@ Edge.prototype.setProperties = function(properties) {
if (!properties) { if (!properties) {
return; return;
} }
this.properties = properties;
var fields = ['style','fontSize','fontFace','fontColor','fontFill','fontStrokeWidth','fontStrokeColor','width', var fields = ['style','fontSize','fontFace','fontColor','fontFill','fontStrokeWidth','fontStrokeColor','width',
'widthSelectionMultiplier','hoverWidth','arrowScaleFactor','dash','inheritColor','labelAlignment', 'opacity', 'widthSelectionMultiplier','hoverWidth','arrowScaleFactor','dash','inheritColor','labelAlignment', 'opacity',
'customScalingFunction','useGradients'
'customScalingFunction','useGradients','value'
]; ];
util.selectiveDeepExtend(fields, this.options, properties); util.selectiveDeepExtend(fields, this.options, properties);
@ -135,9 +137,9 @@ Edge.prototype.connect = function () {
this.from = this.network.nodes[this.fromId] || null; this.from = this.network.nodes[this.fromId] || null;
this.to = this.network.nodes[this.toId] || null; this.to = this.network.nodes[this.toId] || null;
this.connected = (this.from && this.to);
this.connected = (this.from !== null && this.to !== null);
if (this.connected) {
if (this.connected === true) {
this.from.attachEdge(this); this.from.attachEdge(this);
this.to.attachEdge(this); this.to.attachEdge(this);
} }
@ -259,6 +261,7 @@ Edge.prototype._getColor = function(ctx) {
} }
if (this.colorDirty === true) { if (this.colorDirty === true) {
if (this.options.inheritColor == "to") { if (this.options.inheritColor == "to") {
colorObj = { colorObj = {
highlight: this.to.options.color.highlight.border, highlight: this.to.options.color.highlight.border,

+ 41
- 35
lib/network/Network.js View File

@ -85,6 +85,7 @@ function Network (container, data, options) {
fontSizeMin: 14, fontSizeMin: 14,
fontSizeMax: 30, fontSizeMax: 30,
fontSizeMaxVisible: 30, fontSizeMaxVisible: 30,
value: 1,
level: -1, level: -1,
color: { color: {
border: '#2B7CE9', border: '#2B7CE9',
@ -109,6 +110,7 @@ function Network (container, data, options) {
width: 1, width: 1,
widthSelectionMultiplier: 2, widthSelectionMultiplier: 2,
hoverWidth: 1.5, hoverWidth: 1.5,
value:1,
style: 'line', style: 'line',
color: { color: {
color:'#848484', color:'#848484',
@ -164,26 +166,33 @@ function Network (container, data, options) {
springConstant: null springConstant: null
}, },
clustering: { // Per Node in Cluster = PNiC clustering: { // Per Node in Cluster = PNiC
enabled: false, // (Boolean) | global on/off switch for clustering.
initialMaxNodes: 100, // (# nodes) | if the initial amount of nodes is larger than this, we cluster until the total number is less than this threshold.
clusterThreshold:500, // (# nodes) | during calculate forces, we check if the total number of nodes is larger than this. If it is, cluster until reduced to reduceToNodes
reduceToNodes:300, // (# nodes) | during calculate forces, we check if the total number of nodes is larger than clusterThreshold. If it is, cluster until reduced to this
chainThreshold: 0.4, // (% of all drawn nodes)| maximum percentage of allowed chainnodes (long strings of connected nodes) within all nodes. (lower means less chains).
clusterEdgeThreshold: 20, // (px) | edge length threshold. if smaller, this node is clustered.
sectorThreshold: 100, // (# nodes in cluster) | cluster size threshold. If larger, expanding in own sector.
screenSizeThreshold: 0.2, // (% of canvas) | relative size threshold. If the width or height of a clusternode takes up this much of the screen, decluster node.
fontSizeMultiplier: 4.0, // (px PNiC) | how much the cluster font size grows per node in cluster (in px).
maxFontSize: 1000,
forceAmplification: 0.1, // (multiplier PNiC) | factor of increase fo the repulsion force of a cluster (per node in cluster).
distanceAmplification: 0.1, // (multiplier PNiC) | factor how much the repulsion distance of a cluster increases (per node in cluster).
edgeGrowth: 20, // (px PNiC) | amount of clusterSize connected to the edge is multiplied with this and added to edgeLength.
nodeScaling: {width: 1, // (px PNiC) | growth of the width per node in cluster.
height: 1, // (px PNiC) | growth of the height per node in cluster.
radius: 1}, // (px PNiC) | growth of the radius per node in cluster.
maxNodeSizeIncrements: 600, // (# increments) | max growth of the width per node in cluster.
activeAreaBoxSize: 80, // (px) | box area around the curser where clusters are popped open.
clusterLevelDifference: 2, // used for normalization of the cluster levels
clusterByZoom: true // enable clustering through zooming in and out
enabled: false // (Boolean) | global on/off switch for clustering.
//
//initialMaxNodes: 100, // (# nodes) | if the initial amount of nodes is larger than this, we cluster until the total number is less than this threshold.
//clusterThreshold:500, // (# nodes) | during calculate forces, we check if the total number of nodes is larger than this. If it is, cluster until reduced to reduceToNodes
//reduceToNodes:300, // (# nodes) | during calculate forces, we check if the total number of nodes is larger than clusterThreshold. If it is, cluster until reduced to this
//chainThreshold: 0.4, // (% of all drawn nodes)| maximum percentage of allowed chainnodes (long strings of connected nodes) within all nodes. (lower means less chains).
//clusterEdgeThreshold: 20, // (px) | edge length threshold. if smaller, this node is clustered.
//sectorThreshold: 100, // (# nodes in cluster) | cluster size threshold. If larger, expanding in own sector.
//screenSizeThreshold: 0.2, // (% of canvas) | relative size threshold. If the width or height of a clusternode takes up this much of the screen, decluster node.
//fontSizeMultiplier: 4.0, // (px PNiC) | how much the cluster font size grows per node in cluster (in px).
//maxFontSize: 1000,
//forceAmplification: 0.1, // (multiplier PNiC) | factor of increase fo the repulsion force of a cluster (per node in cluster).
//distanceAmplification: 0.1, // (multiplier PNiC) | factor how much the repulsion distance of a cluster increases (per node in cluster).
//edgeGrowth: 20, // (px PNiC) | amount of clusterSize connected to the edge is multiplied with this and added to edgeLength.
//nodeScaling: {width: 1, // (px PNiC) | growth of the width per node in cluster.
// height: 1, // (px PNiC) | growth of the height per node in cluster.
// radius: 1}, // (px PNiC) | growth of the radius per node in cluster.
//maxNodeSizeIncrements: 600, // (# increments) | max growth of the width per node in cluster.
//activeAreaBoxSize: 80, // (px) | box area around the curser where clusters are popped open.
//clusterLevelDifference: 2, // used for normalization of the cluster levels
//clusterByZoom: true // enable clustering through zooming in and out
}, },
navigation: { navigation: {
enabled: false enabled: false
@ -583,11 +592,7 @@ Network.prototype.zoomExtent = function(options, initialZoom, disableStart) {
*/ */
Network.prototype._updateNodeIndexList = function() { Network.prototype._updateNodeIndexList = function() {
this._clearNodeIndexList(); this._clearNodeIndexList();
for (var idx in this.nodes) {
if (this.nodes.hasOwnProperty(idx)) {
this.nodeIndices.push(idx);
}
}
this.nodeIndices = Object.keys(this.nodes);
}; };
@ -1288,7 +1293,6 @@ Network.prototype._zoom = function(scale, pointer) {
this._setScale(scale); this._setScale(scale);
this._setTranslation(tx, ty); this._setTranslation(tx, ty);
this.updateClustersDefault();
if (preScaleDragPointer != null) { if (preScaleDragPointer != null) {
var postScaleDragPointer = this.canvasToDOM(preScaleDragPointer); var postScaleDragPointer = this.canvasToDOM(preScaleDragPointer);
@ -1483,7 +1487,7 @@ Network.prototype._checkShowPopup = function (pointer) {
for (id in edges) { for (id in edges) {
if (edges.hasOwnProperty(id)) { if (edges.hasOwnProperty(id)) {
var edge = edges[id]; var edge = edges[id];
if (edge.connected && (edge.getTitle() !== undefined) &&
if (edge.connected === true && (edge.getTitle() !== undefined) &&
edge.isOverlappingWith(obj)) { edge.isOverlappingWith(obj)) {
overlappingEdges.push(id); overlappingEdges.push(id);
} }
@ -1678,7 +1682,6 @@ Network.prototype._addNodes = function(ids) {
this._updateCalculationNodes(); this._updateCalculationNodes();
this._reconnectEdges(); this._reconnectEdges();
this._updateValueRange(this.nodes); this._updateValueRange(this.nodes);
this.updateLabels();
}; };
/** /**
@ -1914,7 +1917,6 @@ Network.prototype._reconnectEdges = function() {
for (id in nodes) { for (id in nodes) {
if (nodes.hasOwnProperty(id)) { if (nodes.hasOwnProperty(id)) {
nodes[id].edges = []; nodes[id].edges = [];
nodes[id].dynamicEdges = [];
} }
} }
@ -2035,7 +2037,7 @@ Network.prototype._redraw = function(hidden, requested) {
} }
} }
// this._doInSupportSector("_drawNodes",ctx,true);
//this._doInSupportSector("_drawNodes",ctx,true);
// this._drawTree(ctx,"#F00F0F"); // this._drawTree(ctx,"#F00F0F");
// restore original scaling and translation // restore original scaling and translation
@ -2215,7 +2217,7 @@ Network.prototype._drawEdges = function(ctx) {
if (edges.hasOwnProperty(id)) { if (edges.hasOwnProperty(id)) {
var edge = edges[id]; var edge = edges[id];
edge.setScale(this.scale); edge.setScale(this.scale);
if (edge.connected) {
if (edge.connected === true) {
edges[id].draw(ctx); edges[id].draw(ctx);
} }
} }
@ -2442,6 +2444,7 @@ Network.prototype._animationStep = function() {
// check if the physics have settled // check if the physics have settled
if (this.moving == true) { if (this.moving == true) {
var startTime = Date.now(); var startTime = Date.now();
this._physicsTick(); this._physicsTick();
var physicsTime = Date.now() - startTime; var physicsTime = Date.now() - startTime;
@ -2595,11 +2598,14 @@ Network.prototype._configureSmoothCurves = function(disableStart) {
* *
* @private * @private
*/ */
Network.prototype._createBezierNodes = function() {
Network.prototype._createBezierNodes = function(specificEdges) {
if (specificEdges === undefined) {
specificEdges = this.edges;
}
if (this.constants.smoothCurves.enabled == true && this.constants.smoothCurves.dynamic == true) { if (this.constants.smoothCurves.enabled == true && this.constants.smoothCurves.dynamic == true) {
for (var edgeId in this.edges) {
if (this.edges.hasOwnProperty(edgeId)) {
var edge = this.edges[edgeId];
for (var edgeId in specificEdges) {
if (specificEdges.hasOwnProperty(edgeId)) {
var edge = specificEdges[edgeId];
if (edge.via == null) { if (edge.via == null) {
var nodeId = "edgeId:".concat(edge.id); var nodeId = "edgeId:".concat(edge.id);
this.sectors['support']['nodes'][nodeId] = new Node( this.sectors['support']['nodes'][nodeId] = new Node(

+ 11
- 156
lib/network/Node.js View File

@ -33,8 +33,6 @@ function Node(properties, imagelist, grouplist, networkConstants) {
this.hover = false; this.hover = false;
this.edges = []; // all edges connected to this node this.edges = []; // all edges connected to this node
this.dynamicEdges = [];
this.reroutedEdges = {};
// set defaults for the properties // set defaults for the properties
this.id = undefined; this.id = undefined;
@ -72,15 +70,6 @@ function Node(properties, imagelist, grouplist, networkConstants) {
this.setProperties(properties, constants); this.setProperties(properties, constants);
// creating the variables for clustering
this.resetCluster();
this.clusterSession = 0;
this.clusterSizeWidthFactor = networkConstants.clustering.nodeScaling.width;
this.clusterSizeHeightFactor = networkConstants.clustering.nodeScaling.height;
this.clusterSizeRadiusFactor = networkConstants.clustering.nodeScaling.radius;
this.maxNodeSizeIncrements = networkConstants.clustering.maxNodeSizeIncrements;
this.growthIndicator = 0;
// variables to tell the node about the network. // variables to tell the node about the network.
this.networkScaleInv = 1; this.networkScaleInv = 1;
this.networkScale = 1; this.networkScale = 1;
@ -101,18 +90,6 @@ Node.prototype.revertPosition = function() {
} }
/**
* (re)setting the clustering variables and objects
*/
Node.prototype.resetCluster = function() {
// clustering variables
this.formationScale = undefined; // this is used to determine when to open the cluster
this.clusterSize = 1; // this signifies the total amount of nodes in this cluster
this.containedNodes = {};
this.containedEdges = {};
this.clusterSessions = [];
};
/** /**
* Attach a edge to the node * Attach a edge to the node
* @param {Edge} edge * @param {Edge} edge
@ -121,9 +98,6 @@ Node.prototype.attachEdge = function(edge) {
if (this.edges.indexOf(edge) == -1) { if (this.edges.indexOf(edge) == -1) {
this.edges.push(edge); this.edges.push(edge);
} }
if (this.dynamicEdges.indexOf(edge) == -1) {
this.dynamicEdges.push(edge);
}
}; };
/** /**
@ -135,10 +109,6 @@ Node.prototype.detachEdge = function(edge) {
if (index != -1) { if (index != -1) {
this.edges.splice(index, 1); this.edges.splice(index, 1);
} }
index = this.dynamicEdges.indexOf(edge);
if (index != -1) {
this.dynamicEdges.splice(index, 1);
}
}; };
@ -151,10 +121,12 @@ Node.prototype.setProperties = function(properties, constants) {
if (!properties) { if (!properties) {
return; return;
} }
this.properties = properties;
var fields = ['borderWidth','borderWidthSelected','shape','image','brokenImage','radius','fontColor',
'fontSize','fontFace','fontFill','fontStrokeWidth','fontStrokeColor','group','mass','fontDrawThreshold',
'scaleFontWithValue','fontSizeMaxVisible','customScalingFunction','iconFontFace', 'icon', 'iconColor', 'iconSize'
var fields = ['borderWidth', 'borderWidthSelected', 'shape', 'image', 'brokenImage', 'radius', 'fontColor',
'fontSize', 'fontFace', 'fontFill', 'fontStrokeWidth', 'fontStrokeColor', 'group', 'mass', 'fontDrawThreshold',
'scaleFontWithValue', 'fontSizeMaxVisible', 'customScalingFunction', 'iconFontFace', 'icon', 'iconColor', 'iconSize',
'value'
]; ];
util.selectiveDeepExtend(fields, this.options, properties); util.selectiveDeepExtend(fields, this.options, properties);
@ -424,6 +396,7 @@ Node.prototype.discreteStepLimited = function(interval, maxVelocity) {
this.fy = 0; this.fy = 0;
this.vy = 0; this.vy = 0;
} }
}; };
/** /**
@ -547,29 +520,11 @@ Node.prototype._resizeImage = function (ctx) {
} }
this.width = width; this.width = width;
this.height = height; this.height = height;
this.growthIndicator = 0;
if (this.width > 0 && this.height > 0) {
this.width += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeWidthFactor;
this.height += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeHeightFactor;
this.options.radius+= Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeRadiusFactor;
this.growthIndicator = this.width - width;
}
} }
}; };
Node.prototype._drawImageAtPosition = function (ctx) { Node.prototype._drawImageAtPosition = function (ctx) {
if (this.imageObj.width != 0 ) { if (this.imageObj.width != 0 ) {
// draw the shade
if (this.clusterSize > 1) {
var lineWidth = ((this.clusterSize > 1) ? 10 : 0.0);
lineWidth *= this.networkScaleInv;
lineWidth = Math.min(0.2 * this.width,lineWidth);
ctx.globalAlpha = 0.5;
ctx.drawImage(this.imageObj, this.left - lineWidth, this.top - lineWidth, this.width + 2*lineWidth, this.height + 2*lineWidth);
}
// draw the image // draw the image
ctx.globalAlpha = 1.0; ctx.globalAlpha = 1.0;
ctx.drawImage(this.imageObj, this.left, this.top, this.width, this.height); ctx.drawImage(this.imageObj, this.left, this.top, this.width, this.height);
@ -619,12 +574,6 @@ Node.prototype._resizeCircularImage = function (ctx) {
var diameter = this.options.radius * 2; var diameter = this.options.radius * 2;
this.width = diameter; this.width = diameter;
this.height = diameter; this.height = diameter;
// scaling used for clustering
//this.width += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeWidthFactor;
//this.height += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeHeightFactor;
this.options.radius += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeRadiusFactor;
this.growthIndicator = this.options.radius- 0.5*diameter;
this._swapToImageResizeWhenImageLoaded = true; this._swapToImageResizeWhenImageLoaded = true;
} }
} }
@ -678,12 +627,6 @@ Node.prototype._resizeBox = function (ctx) {
var textSize = this.getTextSize(ctx); var textSize = this.getTextSize(ctx);
this.width = textSize.width + 2 * margin; this.width = textSize.width + 2 * margin;
this.height = textSize.height + 2 * margin; this.height = textSize.height + 2 * margin;
this.width += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeWidthFactor;
this.height += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeHeightFactor;
this.growthIndicator = this.width - (textSize.width + 2 * margin);
// this.options.radius+= Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeRadiusFactor;
} }
}; };
@ -693,22 +636,11 @@ Node.prototype._drawBox = function (ctx) {
this.left = this.x - this.width / 2; this.left = this.x - this.width / 2;
this.top = this.y - this.height / 2; this.top = this.y - this.height / 2;
var clusterLineWidth = 2.5;
var borderWidth = this.options.borderWidth; var borderWidth = this.options.borderWidth;
var selectionLineWidth = this.options.borderWidthSelected || 2 * 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.strokeStyle = this.selected ? this.options.color.highlight.border : this.hover ? this.options.color.hover.border : this.options.color.border;
// draw the outer border
if (this.clusterSize > 1) {
ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0);
ctx.lineWidth *= this.networkScaleInv;
ctx.lineWidth = Math.min(this.width,ctx.lineWidth);
ctx.roundRect(this.left-2*ctx.lineWidth, this.top-2*ctx.lineWidth, this.width+4*ctx.lineWidth, this.height+4*ctx.lineWidth, this.options.radius);
ctx.stroke();
}
ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0);
ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth);
ctx.lineWidth *= this.networkScaleInv; ctx.lineWidth *= this.networkScaleInv;
ctx.lineWidth = Math.min(this.width,ctx.lineWidth); ctx.lineWidth = Math.min(this.width,ctx.lineWidth);
@ -734,12 +666,6 @@ Node.prototype._resizeDatabase = function (ctx) {
var size = textSize.width + 2 * margin; var size = textSize.width + 2 * margin;
this.width = size; this.width = size;
this.height = size; this.height = size;
// scaling used for clustering
this.width += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeWidthFactor;
this.height += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeHeightFactor;
this.options.radius+= Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeRadiusFactor;
this.growthIndicator = this.width - size;
} }
}; };
@ -748,22 +674,11 @@ Node.prototype._drawDatabase = function (ctx) {
this.left = this.x - this.width / 2; this.left = this.x - this.width / 2;
this.top = this.y - this.height / 2; this.top = this.y - this.height / 2;
var clusterLineWidth = 2.5;
var borderWidth = this.options.borderWidth; var borderWidth = this.options.borderWidth;
var selectionLineWidth = this.options.borderWidthSelected || 2 * 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.strokeStyle = this.selected ? this.options.color.highlight.border : this.hover ? this.options.color.hover.border : this.options.color.border;
// draw the outer border
if (this.clusterSize > 1) {
ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0);
ctx.lineWidth *= this.networkScaleInv;
ctx.lineWidth = Math.min(this.width,ctx.lineWidth);
ctx.database(this.x - this.width/2 - 2*ctx.lineWidth, this.y - this.height*0.5 - 2*ctx.lineWidth, this.width + 4*ctx.lineWidth, this.height + 4*ctx.lineWidth);
ctx.stroke();
}
ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0);
ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth);
ctx.lineWidth *= this.networkScaleInv; ctx.lineWidth *= this.networkScaleInv;
ctx.lineWidth = Math.min(this.width,ctx.lineWidth); ctx.lineWidth = Math.min(this.width,ctx.lineWidth);
@ -790,32 +705,16 @@ Node.prototype._resizeCircle = function (ctx) {
this.width = diameter; this.width = diameter;
this.height = diameter; this.height = diameter;
// scaling used for clustering
// this.width += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeWidthFactor;
// this.height += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeHeightFactor;
this.options.radius += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeRadiusFactor;
this.growthIndicator = this.options.radius- 0.5*diameter;
} }
}; };
Node.prototype._drawRawCircle = function (ctx, x, y, radius) { Node.prototype._drawRawCircle = function (ctx, x, y, radius) {
var clusterLineWidth = 2.5;
var borderWidth = this.options.borderWidth; var borderWidth = this.options.borderWidth;
var selectionLineWidth = this.options.borderWidthSelected || 2 * 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.strokeStyle = this.selected ? this.options.color.highlight.border : this.hover ? this.options.color.hover.border : this.options.color.border;
// draw the outer border
if (this.clusterSize > 1) {
ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0);
ctx.lineWidth *= this.networkScaleInv;
ctx.lineWidth = Math.min(this.width,ctx.lineWidth);
ctx.circle(x, y, radius+2*ctx.lineWidth);
ctx.stroke();
}
ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0);
ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth);
ctx.lineWidth *= this.networkScaleInv; ctx.lineWidth *= this.networkScaleInv;
ctx.lineWidth = Math.min(this.width,ctx.lineWidth); ctx.lineWidth = Math.min(this.width,ctx.lineWidth);
@ -850,12 +749,6 @@ Node.prototype._resizeEllipse = function (ctx) {
this.width = this.height; this.width = this.height;
} }
var defaultSize = this.width; var defaultSize = this.width;
// scaling used for clustering
this.width += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeWidthFactor;
this.height += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeHeightFactor;
this.options.radius += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeRadiusFactor;
this.growthIndicator = this.width - defaultSize;
} }
}; };
@ -864,22 +757,12 @@ Node.prototype._drawEllipse = function (ctx) {
this.left = this.x - this.width / 2; this.left = this.x - this.width / 2;
this.top = this.y - this.height / 2; this.top = this.y - this.height / 2;
var clusterLineWidth = 2.5;
var borderWidth = this.options.borderWidth; var borderWidth = this.options.borderWidth;
var selectionLineWidth = this.options.borderWidthSelected || 2 * 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.strokeStyle = this.selected ? this.options.color.highlight.border : this.hover ? this.options.color.hover.border : this.options.color.border;
// draw the outer border
if (this.clusterSize > 1) {
ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0);
ctx.lineWidth *= this.networkScaleInv;
ctx.lineWidth = Math.min(this.width,ctx.lineWidth);
ctx.ellipse(this.left-2*ctx.lineWidth, this.top-2*ctx.lineWidth, this.width+4*ctx.lineWidth, this.height+4*ctx.lineWidth);
ctx.stroke();
}
ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0);
ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth);
ctx.lineWidth *= this.networkScaleInv; ctx.lineWidth *= this.networkScaleInv;
ctx.lineWidth = Math.min(this.width,ctx.lineWidth); ctx.lineWidth = Math.min(this.width,ctx.lineWidth);
@ -923,12 +806,6 @@ Node.prototype._resizeShape = function (ctx) {
var size = 2 * this.options.radius; var size = 2 * this.options.radius;
this.width = size; this.width = size;
this.height = size; this.height = size;
// scaling used for clustering
this.width += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeWidthFactor;
this.height += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeHeightFactor;
this.options.radius+= Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeRadiusFactor;
this.growthIndicator = this.width - size;
} }
}; };
@ -938,7 +815,6 @@ Node.prototype._drawShape = function (ctx, shape) {
this.left = this.x - this.width / 2; this.left = this.x - this.width / 2;
this.top = this.y - this.height / 2; this.top = this.y - this.height / 2;
var clusterLineWidth = 2.5;
var borderWidth = this.options.borderWidth; var borderWidth = this.options.borderWidth;
var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth; var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth;
var radiusMultiplier = 2; var radiusMultiplier = 2;
@ -953,16 +829,7 @@ Node.prototype._drawShape = function (ctx, shape) {
} }
ctx.strokeStyle = this.selected ? this.options.color.highlight.border : this.hover ? this.options.color.hover.border : this.options.color.border; ctx.strokeStyle = this.selected ? this.options.color.highlight.border : this.hover ? this.options.color.hover.border : this.options.color.border;
// draw the outer border
if (this.clusterSize > 1) {
ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0);
ctx.lineWidth *= this.networkScaleInv;
ctx.lineWidth = Math.min(this.width,ctx.lineWidth);
ctx[shape](this.x, this.y, this.options.radius+ radiusMultiplier * ctx.lineWidth);
ctx.stroke();
}
ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0);
ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth);
ctx.lineWidth *= this.networkScaleInv; ctx.lineWidth *= this.networkScaleInv;
ctx.lineWidth = Math.min(this.width,ctx.lineWidth); ctx.lineWidth = Math.min(this.width,ctx.lineWidth);
@ -990,12 +857,6 @@ Node.prototype._resizeText = function (ctx) {
var textSize = this.getTextSize(ctx); var textSize = this.getTextSize(ctx);
this.width = textSize.width + 2 * margin; this.width = textSize.width + 2 * margin;
this.height = textSize.height + 2 * margin; this.height = textSize.height + 2 * margin;
// scaling used for clustering
this.width += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeWidthFactor;
this.height += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeHeightFactor;
this.options.radius+= Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeRadiusFactor;
this.growthIndicator = this.width - (textSize.width + 2 * margin);
} }
}; };
@ -1022,12 +883,6 @@ Node.prototype._resizeIcon = function (ctx) {
}; };
this.width = iconSize.width + 2 * margin; this.width = iconSize.width + 2 * margin;
this.height = iconSize.height + 2 * margin; this.height = iconSize.height + 2 * margin;
// scaling used for clustering
this.width += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeWidthFactor;
this.height += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeHeightFactor;
this.options.radius += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeRadiusFactor;
this.growthIndicator = this.width - (iconSize.width + 2 * margin);
} }
}; };

+ 392
- 961
lib/network/mixins/ClusterMixin.js
File diff suppressed because it is too large
View File


+ 6
- 6
lib/network/mixins/SelectionMixin.js View File

@ -355,8 +355,8 @@ exports._clusterInSelection = function() {
* @private * @private
*/ */
exports._selectConnectedEdges = function(node) { exports._selectConnectedEdges = function(node) {
for (var i = 0; i < node.dynamicEdges.length; i++) {
var edge = node.dynamicEdges[i];
for (var i = 0; i < node.edges.length; i++) {
var edge = node.edges[i];
edge.select(); edge.select();
this._addToSelection(edge); this._addToSelection(edge);
} }
@ -369,8 +369,8 @@ exports._selectConnectedEdges = function(node) {
* @private * @private
*/ */
exports._hoverConnectedEdges = function(node) { exports._hoverConnectedEdges = function(node) {
for (var i = 0; i < node.dynamicEdges.length; i++) {
var edge = node.dynamicEdges[i];
for (var i = 0; i < node.edges.length; i++) {
var edge = node.edges[i];
edge.hover = true; edge.hover = true;
this._addToHover(edge); this._addToHover(edge);
} }
@ -384,8 +384,8 @@ exports._hoverConnectedEdges = function(node) {
* @private * @private
*/ */
exports._unselectConnectedEdges = function(node) { exports._unselectConnectedEdges = function(node) {
for (var i = 0; i < node.dynamicEdges.length; i++) {
var edge = node.dynamicEdges[i];
for (var i = 0; i < node.edges.length; i++) {
var edge = node.edges[i];
edge.unselect(); edge.unselect();
this._removeFromSelection(edge); this._removeFromSelection(edge);
} }

+ 1
- 1
lib/network/mixins/physics/HierarchialRepulsionMixin.js View File

@ -81,7 +81,7 @@ exports._calculateHierarchicalSpringForces = function () {
for (edgeId in edges) { for (edgeId in edges) {
if (edges.hasOwnProperty(edgeId)) { if (edges.hasOwnProperty(edgeId)) {
edge = edges[edgeId]; edge = edges[edgeId];
if (edge.connected) {
if (edge.connected === true) {
// only calculate forces if nodes are in the same sector // only calculate forces if nodes are in the same sector
if (this.nodes.hasOwnProperty(edge.toId) && this.nodes.hasOwnProperty(edge.fromId)) { if (this.nodes.hasOwnProperty(edge.toId) && this.nodes.hasOwnProperty(edge.fromId)) {
edgeLength = edge.physics.springLength; edgeLength = edge.physics.springLength;

+ 3
- 14
lib/network/mixins/physics/PhysicsMixin.js View File

@ -71,11 +71,6 @@ exports._initializeForceCalculation = function () {
this.nodes[this.nodeIndices[0]]._setForce(0, 0); this.nodes[this.nodeIndices[0]]._setForce(0, 0);
} }
else { else {
// if there are too many nodes on screen, we cluster without repositioning
if (this.nodeIndices.length > this.constants.clustering.clusterThreshold && this.constants.clustering.enabled == true) {
this.clusterToFit(this.constants.clustering.reduceToNodes, false);
}
// we now start the force calculation // we now start the force calculation
this._calculateForces(); this._calculateForces();
} }
@ -202,12 +197,10 @@ exports._calculateSpringForces = function () {
for (edgeId in edges) { for (edgeId in edges) {
if (edges.hasOwnProperty(edgeId)) { if (edges.hasOwnProperty(edgeId)) {
edge = edges[edgeId]; edge = edges[edgeId];
if (edge.connected) {
if (edge.connected === true) {
// only calculate forces if nodes are in the same sector // only calculate forces if nodes are in the same sector
if (this.nodes.hasOwnProperty(edge.toId) && this.nodes.hasOwnProperty(edge.fromId)) { if (this.nodes.hasOwnProperty(edge.toId) && this.nodes.hasOwnProperty(edge.fromId)) {
edgeLength = edge.physics.springLength; edgeLength = edge.physics.springLength;
// this implies that the edges between big clusters are longer
edgeLength += (edge.to.clusterSize + edge.from.clusterSize - 2) * this.constants.clustering.edgeGrowth;
dx = (edge.from.x - edge.to.x); dx = (edge.from.x - edge.to.x);
dy = (edge.from.y - edge.to.y); dy = (edge.from.y - edge.to.y);
@ -242,14 +235,14 @@ exports._calculateSpringForces = function () {
* @private * @private
*/ */
exports._calculateSpringForcesWithSupport = function () { exports._calculateSpringForcesWithSupport = function () {
var edgeLength, edge, edgeId, combinedClusterSize;
var edgeLength, edge, edgeId;
var edges = this.edges; var edges = this.edges;
// forces caused by the edges, modelled as springs // forces caused by the edges, modelled as springs
for (edgeId in edges) { for (edgeId in edges) {
if (edges.hasOwnProperty(edgeId)) { if (edges.hasOwnProperty(edgeId)) {
edge = edges[edgeId]; edge = edges[edgeId];
if (edge.connected) {
if (edge.connected === true) {
// only calculate forces if nodes are in the same sector // only calculate forces if nodes are in the same sector
if (this.nodes.hasOwnProperty(edge.toId) && this.nodes.hasOwnProperty(edge.fromId)) { if (this.nodes.hasOwnProperty(edge.toId) && this.nodes.hasOwnProperty(edge.fromId)) {
if (edge.via != null) { if (edge.via != null) {
@ -259,10 +252,6 @@ exports._calculateSpringForcesWithSupport = function () {
edgeLength = edge.physics.springLength; edgeLength = edge.physics.springLength;
combinedClusterSize = node1.clusterSize + node3.clusterSize - 2;
// this implies that the edges between big clusters are longer
edgeLength += combinedClusterSize * this.constants.clustering.edgeGrowth;
this._calculateSpringForce(node1, node2, 0.5 * edgeLength); this._calculateSpringForce(node1, node2, 0.5 * edgeLength);
this._calculateSpringForce(node2, node3, 0.5 * edgeLength); this._calculateSpringForce(node2, node3, 0.5 * edgeLength);
} }

+ 17
- 0
lib/network/modules/ClusterEngine.js View File

@ -0,0 +1,17 @@
/**
* Created by Alex on 2/20/2015.
*/
var public = require("./clustering/public");
var support = require("./clustering/support");
var backend = require("./clustering/backend");
function ClusterEngine(network) {
this.network = network;
}
module.exports = clusterEngine

+ 0
- 0
lib/network/modules/clustering/backend.js View File


+ 0
- 0
lib/network/modules/clustering/public.js View File


+ 0
- 0
lib/network/modules/clustering/support.js View File


+ 4
- 2
lib/timeline/component/graph2d_types/bar.js View File

@ -58,7 +58,8 @@ Bargraph.draw = function (groupIds, processedGroupData, framework) {
combinedData.push({ combinedData.push({
x: processedGroupData[groupIds[i]][j].x, x: processedGroupData[groupIds[i]][j].x,
y: processedGroupData[groupIds[i]][j].y, y: processedGroupData[groupIds[i]][j].y,
groupId: groupIds[i]
groupId: groupIds[i],
label: processedGroupData[groupIds[i]][j].label
}); });
barPoints += 1; barPoints += 1;
} }
@ -114,7 +115,8 @@ Bargraph.draw = function (groupIds, processedGroupData, framework) {
DOMutil.drawBar(combinedData[i].x + drawData.offset, combinedData[i].y - heightOffset, drawData.width, group.zeroPosition - combinedData[i].y, group.className + ' bar', framework.svgElements, framework.svg); DOMutil.drawBar(combinedData[i].x + drawData.offset, combinedData[i].y - heightOffset, drawData.width, group.zeroPosition - combinedData[i].y, group.className + ' bar', framework.svgElements, framework.svg);
// draw points // draw points
if (group.options.drawPoints.enabled == true) { if (group.options.drawPoints.enabled == true) {
DOMutil.drawPoint(combinedData[i].x + drawData.offset, combinedData[i].y, group, framework.svgElements, framework.svg);
Points.draw([combinedData[i]], group, framework, drawData.offset);
//DOMutil.drawPoint(combinedData[i].x + drawData.offset, combinedData[i].y, group, framework.svgElements, framework.svg);
} }
} }
}; };

+ 5
- 3
lib/util.js View File

@ -225,22 +225,24 @@ exports.selectiveNotDeepExtend = function (props, a, b) {
* Deep extend an object a with the properties of object b * Deep extend an object a with the properties of object b
* @param {Object} a * @param {Object} a
* @param {Object} b * @param {Object} b
* @param {Boolean} protoExtend --> optional parameter. If true, the prototype values will also be extended.
* (ie. the options objects that inherit from others will also get the inherited options)
* @returns {Object} * @returns {Object}
*/ */
exports.deepExtend = function(a, b) {
exports.deepExtend = function(a, b, protoExtend) {
// TODO: add support for Arrays to deepExtend // TODO: add support for Arrays to deepExtend
if (Array.isArray(b)) { if (Array.isArray(b)) {
throw new TypeError('Arrays are not supported by deepExtend'); throw new TypeError('Arrays are not supported by deepExtend');
} }
for (var prop in b) { for (var prop in b) {
if (b.hasOwnProperty(prop)) {
if (b.hasOwnProperty(prop) || protoExtend === true) {
if (b[prop] && b[prop].constructor === Object) { if (b[prop] && b[prop].constructor === Object) {
if (a[prop] === undefined) { if (a[prop] === undefined) {
a[prop] = {}; a[prop] = {};
} }
if (a[prop].constructor === Object) { if (a[prop].constructor === Object) {
exports.deepExtend(a[prop], b[prop]);
exports.deepExtend(a[prop], b[prop], protoExtend);
} }
else { else {
a[prop] = b[prop]; a[prop] = b[prop];

Loading…
Cancel
Save