Browse Source

Added getBaseEdge, getClusteredEdges updateEdge and updateClusteredNo… (#2055)

* Added getBaseEdge, getClusteredEdges updateEdge and updateClusteredNode to allow option changes of the clustered edges as they are no longer the original base Edge

changing edge color is a good example.
i.e network.clustering.updateEdge(originalEdge.id, {color : '#aa0000'});
will now update all edges including the ones that were created when clustering.

Also when clicking on a clustered edge the event returns the clustered Edge id, which doesn't mean a lot.
This can be now converted back into the original edge with
network.clustering.getBaseEdge(clusteredEdge.id)

Similar with updateClusteredNode
This new method allows the options to be changed on a clustered Node.

i.e clicking on a clustered node gives its clustered node id.
Want to change the clustered image to a star then do this
 network.clustering.updateClusteredNode(clusteredNodeId, {shape : 'star'});

* Updated docs with getClusteredEdge, getBaseEdge, updateEdge and updateClusteredNode

Also added method subsections into contents

* Added better error handling to updateEdge and updateClusteredNodes

corrected errors spotted by @mojoaxel

* Added example html demonstrating getBaseEdge, getClusteredEdges updateEdge and updateClusteredNode
codeClimate
Steven 8 years ago
committed by Alexander Wunschik
parent
commit
b752755a36
3 changed files with 247 additions and 17 deletions
  1. +67
    -11
      docs/network/index.html
  2. +107
    -0
      examples/network/other/changingClusteredEdgesNodes.html
  3. +73
    -6
      lib/network/modules/Clustering.js

+ 67
- 11
docs/network/index.html View File

@ -210,7 +210,20 @@
<ul> <ul>
<li><a href="#modules">Modules</a></li> <li><a href="#modules">Modules</a></li>
<li><a href="#options">Options</a></li> <li><a href="#options">Options</a></li>
<li><a href="#methods">Methods</a></li>
<li><a href="#methods">Method Reference</a>
<ul>
<li><a href="#methodGlobal">Global</a></li>
<li><a href="#methodCanvas">Canvas</a></li>
<li><a href="#methodClustering">Clustering</a></li>
<li><a href="#methodLayout">Layout</a></li>
<li><a href="#methodManipulation">Manipulation</a></li>
<li><a href="#methodInformation">Information</a></li>
<li><a href="#methodPhysics">Physics</a></li>
<li><a href="#methodSelection">Selection</a></li>
<li><a href="#methodViewport">Viewport</a></li>
<li><a href="#methodConfigurator">Configurator</a></li>
</ul>
</li>
<li><a href="#Events">Events</a></li> <li><a href="#Events">Events</a></li>
<li><a href="#importing_data">Importing Data</a> <li><a href="#importing_data">Importing Data</a>
<ul> <ul>
@ -420,7 +433,7 @@ var locales = {
the the
modules listed above.</p> modules listed above.</p>
<table class="methods-collapsable" id="methodTable"> <table class="methods-collapsable" id="methodTable">
<tr class="subHeader">
<tr id="methodGlobal" class="subHeader">
<td colspan="2">Global methods for the network.</td> <td colspan="2">Global methods for the network.</td>
</tr> </tr>
<tr class="collapsible toggle" onclick="toggleTable('methodTable','destroy', this);"> <tr class="collapsible toggle" onclick="toggleTable('methodTable','destroy', this);">
@ -485,7 +498,7 @@ var locales = {
</tr> </tr>
<tr class="subHeader">
<tr id="methodCanvas" class="subHeader">
<td colspan="2">Methods related to the canvas.</td> <td colspan="2">Methods related to the canvas.</td>
</tr> </tr>
<tr class="collapsible toggle" onclick="toggleTable('methodTable','canvasToDOM', this);"> <tr class="collapsible toggle" onclick="toggleTable('methodTable','canvasToDOM', this);">
@ -532,7 +545,7 @@ var locales = {
<td>Set the size of the canvas. This is automatically done on a window resize.</td> <td>Set the size of the canvas. This is automatically done on a window resize.</td>
</tr> </tr>
<tr class="subHeader">
<tr id="methodClustering" class="subHeader">
<td colspan="2">Clustering</td> <td colspan="2">Clustering</td>
</tr> </tr>
<tr class="collapsible toggle" onclick="toggleTable('methodTable','cluster', this);"> <tr class="collapsible toggle" onclick="toggleTable('methodTable','cluster', this);">
@ -607,6 +620,49 @@ var locales = {
<code>network.clustering.findNode('fred')</code> will return <code>['A','B','C','fred']</code>. <code>network.clustering.findNode('fred')</code> will return <code>['A','B','C','fred']</code>.
</td> </td>
</tr> </tr>
<tr class="collapsible toggle" onclick="toggleTable('methodTable','getClusteredEdges', this);">
<td colspan="2"><span parent="getClusteredEdges" class="right-caret" id="method_getClusteredEdges"></span> getClusteredEdges(
<code>String baseEdgeId</code>)
</tr>
<tr class="hidden" parent="getClusteredEdges">
<td class="midMethods">Returns: Array</td>
<td>Similiar to <code>findNode</code> in that it returns all the edge ids that were created from the provided edge during clustering
</td>
</tr>
<tr class="collapsible toggle" onclick="toggleTable('methodTable','getBaseEdge', this);">
<td colspan="2"><span parent="getBaseEdge" class="right-caret" id="method_getBaseEdge"></span> getBaseEdge(
<code>String clusteredEdgeId</code>)
</tr>
<tr class="hidden" parent="getBaseEdge">
<td class="midMethods">Returns: Value</td>
<td>When a clusteredEdgeId is available, this method will return the original baseEdgeId provided in <code>data.edges</code><br/>
ie. After clustering the 'SelectEdge' event is fired but provides only the clustered edge. This method can then be used to return the baseEdgeId.
</td>
</tr>
<tr class="collapsible toggle" onclick="toggleTable('methodTable','updateEdge', this);">
<td colspan="2"><span parent="updateEdge" class="right-caret" id="method_updateEdge"></span> updateEdge(
<code>String startEdgeId, Object options</code>)
</tr>
<tr class="hidden" parent="updateEdge">
<td class="midMethods">Returns: none</td>
<td>Visible edges between clustered nodes are not the same edge as the ones provided in <code>data.edges</code> passed on <code>network</code> creation<br/>
With each layer of clustering, copies of the edges between clusters are created and the previous edges are hidden, until the cluster is opened.<br/>
This method takes an edgeId (ie. a base edgeId from <data>data.edges</code>) and applys the options to it and any edges that were created from it while clustering.<br><br> Example:
<code>network.clustering.updateEdge(originalEdge.id, {color : '#aa0000'});</code><br/>
This would turn the base edge and any subsequent edges red, so when opening clusters the edges will all be the same color.
</td>
</tr>
<tr class="collapsible toggle" onclick="toggleTable('methodTable','updateClusteredNode', this);">
<td colspan="2"><span parent="updateClusteredNode" class="right-caret" id="method_updateClusteredNode"></span> updateClusteredNode(
<code>String clusteredNodeId, Object options</code>)
</tr>
<tr class="hidden" parent="updateClusteredNode">
<td class="midMethods">Returns: none</td>
<td>Clustered Nodes when created are not contained in the original <code>data.nodes</code> passed on <code>network</code> creation<br/>
This method updates the cluster node.<br><br> Example:
<code>network.clustering.updateClusteredNode(clusteredNodeId, {shape : 'star'});</code>
</td>
</tr>
<tr class="collapsible toggle" onclick="toggleTable('methodTable','isCluster', this);"> <tr class="collapsible toggle" onclick="toggleTable('methodTable','isCluster', this);">
<td colspan="2"><span parent="isCluster" class="right-caret" id="method_isCluster"></span> isCluster( <td colspan="2"><span parent="isCluster" class="right-caret" id="method_isCluster"></span> isCluster(
<code>String nodeId</code>) <code>String nodeId</code>)
@ -649,7 +705,7 @@ function releaseFunction (clusterPosition, containedNodesPositions) {
</td> </td>
</tr> </tr>
<tr class="subHeader">
<tr id="methodLayout" class="subHeader">
<td colspan="2">Layout</td> <td colspan="2">Layout</td>
</tr> </tr>
<tr class="collapsible toggle" onclick="toggleTable('methodTable','getSeed', this);"> <tr class="collapsible toggle" onclick="toggleTable('methodTable','getSeed', this);">
@ -666,7 +722,7 @@ function releaseFunction (clusterPosition, containedNodesPositions) {
</tr> </tr>
<tr class="subHeader">
<tr id="methodManipulation" class="subHeader">
<td colspan="2">Manipulation methods to use the manipulation system without GUI.</td> <td colspan="2">Manipulation methods to use the manipulation system without GUI.</td>
</tr> </tr>
<tr class="collapsible toggle" onclick="toggleTable('methodTable','enableEditMode', this);"> <tr class="collapsible toggle" onclick="toggleTable('methodTable','enableEditMode', this);">
@ -727,7 +783,7 @@ function releaseFunction (clusterPosition, containedNodesPositions) {
</tr> </tr>
<tr class="subHeader">
<tr id="methodInformation" class="subHeader">
<td colspan="2">Methods to get information on nodes and edges.</td> <td colspan="2">Methods to get information on nodes and edges.</td>
</tr> </tr>
<tr class="collapsible toggle" onclick="toggleTable('methodTable','getPositions', this);"> <tr class="collapsible toggle" onclick="toggleTable('methodTable','getPositions', this);">
@ -825,7 +881,7 @@ function releaseFunction (clusterPosition, containedNodesPositions) {
</tr> </tr>
<tr class="subHeader">
<tr id="methodPhysics" class="subHeader">
<td colspan="2">Physics methods to control when the simulation should run.</td> <td colspan="2">Physics methods to control when the simulation should run.</td>
</tr> </tr>
<tr class="collapsible toggle" onclick="toggleTable('methodTable','startSimulation', this);"> <tr class="collapsible toggle" onclick="toggleTable('methodTable','startSimulation', this);">
@ -856,7 +912,7 @@ function releaseFunction (clusterPosition, containedNodesPositions) {
</tr> </tr>
<tr class="subHeader">
<tr id="methodSelection" class="subHeader">
<td colspan="2">Selection methods for nodes and edges.</td> <td colspan="2">Selection methods for nodes and edges.</td>
</tr> </tr>
<tr class="collapsible toggle" onclick="toggleTable('methodTable','getSelection', this);"> <tr class="collapsible toggle" onclick="toggleTable('methodTable','getSelection', this);">
@ -971,7 +1027,7 @@ function releaseFunction (clusterPosition, containedNodesPositions) {
</tr> </tr>
<tr class="subHeader">
<tr id="methodViewport" class="subHeader">
<td colspan="2">Methods to control the viewport for zoom and animation.</td> <td colspan="2">Methods to control the viewport for zoom and animation.</td>
</tr> </tr>
<tr class="collapsible toggle" onclick="toggleTable('methodTable','getScale', this);"> <tr class="collapsible toggle" onclick="toggleTable('methodTable','getScale', this);">
@ -1083,7 +1139,7 @@ function releaseFunction (clusterPosition, containedNodesPositions) {
<td class="midMethods">Returns: none</td> <td class="midMethods">Returns: none</td>
<td>Programatically release the focussed node.</td> <td>Programatically release the focussed node.</td>
</tr> </tr>
<tr class="subHeader">
<tr id="methodConfigurator" class="subHeader">
<td colspan="2">Methods to use with the configurator module.</td> <td colspan="2">Methods to use with the configurator module.</td>
</tr> </tr>
<tr class="collapsible toggle" onclick="toggleTable('methodTable','getOptionsFromConfigurator', this);"> <tr class="collapsible toggle" onclick="toggleTable('methodTable','getOptionsFromConfigurator', this);">

+ 107
- 0
examples/network/other/changingClusteredEdgesNodes.html View File

@ -0,0 +1,107 @@
<!doctype html>
<html>
<head>
<title>Network | Clustering</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: 600px;
height: 600px;
border: 1px solid lightgray;
}
p {
max-width:600px;
}
h4 {
margin-bottom:3px;
}
</style>
<script src="../../googleAnalytics.js"></script>
</head>
<body>
<p>
Demonstrating getBaseEdge, getClusteredEdges updateEdge and updateClusteredNode. <br/><ul><li>Clicking on the cluster will change it to a star (updateClusteredNode).</li>
<li>Clicking on an edge will make it red regardless of whether it is a clusteredEdge or not (updateEdge)</li>
<li>Clicking on an edge will also show the results of getBaseEdge and getClusteredEdge</li>
</ul>
</p>
<div id="mynetwork"></div>
<pre id="eventSpan"></pre>
<script type="text/javascript">
// create an array with nodes
var nodes = [
{id: 1, label: 'Node 1', color:'orange'},
{id: 2, label: 'Node 2', color:'DarkViolet', font:{color:'white'}},
{id: 3, label: 'Node 3', color:'orange'},
{id: 4, label: 'Node 4', color:'DarkViolet', font:{color:'white'}},
{id: 5, label: 'Node 5', color:'orange'},
{id: 6, label: 'cid = 1', cid:1, color:'orange'},
{id: 7, label: 'cid = 1', cid:1, color:'DarkViolet', font:{color:'white'}},
{id: 8, label: 'cid = 1', cid:1, color:'lime'},
{id: 9, label: 'cid = 1', cid:1, color:'orange'},
{id: 10, label: 'cid = 1', cid:1, color:'lime'}
];
// 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 = {layout:{randomSeed:8}};
var network = new vis.Network(container, data, options);
var clusterOptionsByData = {
joinCondition:function(childOptions) {
return childOptions.cid == 1;
},
clusterNodeProperties: {id:'cidCluster', borderWidth:3, shape:'database'}
};
network.cluster(clusterOptionsByData);
network.on("selectNode", function(params) {
if (params.nodes.length == 1) {
if (network.isCluster(params.nodes[0]) == true) {
network.clustering.updateClusteredNode(params.nodes[0], {shape : 'star'});
}
}
});
network.on("selectEdge", function(params) {
if (params.edges.length == 1) {
// Single edge selected
var obj = {};
obj.clicked_id = params.edges[0];
network.clustering.updateEdge(params.edges[0], {color : '#aa0000'});
obj.base_edge = network.clustering.getBaseEdge(params.edges[0]);
obj.all_clustered_edges = network.clustering.getClusteredEdges(params.edges[0]);
document.getElementById('eventSpan').innerHTML = '<h2>selectEdge event:</h2>' + JSON.stringify(obj, null, 4);
}
});
</script>
</body>
</html>

+ 73
- 6
lib/network/modules/Clustering.js View File

@ -15,12 +15,6 @@ class ClusterEngine {
this.body.emitter.on('_resetData', () => {this.clusteredNodes = {}; this.clusteredEdges = {};}) this.body.emitter.on('_resetData', () => {this.clusteredNodes = {}; this.clusteredEdges = {};})
} }
setOptions(options) {
if (options !== undefined) {
}
}
/** /**
* *
* @param hubsize * @param hubsize
@ -317,6 +311,9 @@ class ClusterEngine {
let newEdge = this.body.functions.createEdge(clonedOptions); let newEdge = this.body.functions.createEdge(clonedOptions);
newEdge.clusteringEdgeReplacingId = edge.id; newEdge.clusteringEdgeReplacingId = edge.id;
// also reference the new edge in the old edge
this.body.edges[edge.id].edgeReplacedById = newEdge.id;
// connect the edge. // connect the edge.
this.body.edges[newEdge.id] = newEdge; this.body.edges[newEdge.id] = newEdge;
newEdge.connect(); newEdge.connect();
@ -696,6 +693,76 @@ class ClusterEngine {
return stack; return stack;
} }
/**
* Using a clustered nodeId, update with the new options
* @param clusteredNodeId
* @param {object} newOptions
*/
updateClusteredNode(clusteredNodeId, newOptions) {
if (clusteredNodeId === undefined) {throw new Error("No clusteredNodeId supplied to updateClusteredNode.");}
if (newOptions === undefined) {throw new Error("No newOptions supplied to updateClusteredNode.");}
if (this.body.nodes[clusteredNodeId] === undefined) {throw new Error("The clusteredNodeId supplied to updateClusteredNode does not exist.");}
this.body.nodes[clusteredNodeId].setOptions(newOptions);
this.body.emitter.emit('_dataChanged');
}
/**
* Using a base edgeId, update all related clustered edges with the new options
* @param startEdgeId
* @param {object} newOptions
*/
updateEdge(startEdgeId, newOptions) {
if (startEdgeId === undefined) {throw new Error("No startEdgeId supplied to updateEdge.");}
if (newOptions === undefined) {throw new Error("No newOptions supplied to updateEdge.");}
if (this.body.edges[startEdgeId] === undefined) {throw new Error("The startEdgeId supplied to updateEdge does not exist.");}
let allEdgeIds = this.getClusteredEdges(startEdgeId);
for (let i = 0; i < allEdgeIds.length; i++) {
var edge = this.body.edges[allEdgeIds[i]];
edge.setOptions(newOptions);
}
this.body.emitter.emit('_dataChanged');
}
/**
* Get a stack of clusterEdgeId's (+base edgeid) that a base edge is the same as. cluster edge C -> cluster edge B -> cluster edge A -> base edge(edgeId)
* @param edgeId
* @returns {Array}
*/
getClusteredEdges(edgeId) {
let stack = [];
let max = 100;
let counter = 0;
while (edgeId !== undefined && this.body.edges[edgeId] !== undefined && counter < max) {
stack.push(this.body.edges[edgeId].id);
edgeId = this.body.edges[edgeId].edgeReplacedById;
counter++;
}
stack.reverse();
return stack;
}
/**
* Get the base edge id of clusterEdgeId. cluster edge (clusteredEdgeId) -> cluster edge B -> cluster edge C -> base edge
* @param clusteredEdgeId
* @returns baseEdgeId
*/
getBaseEdge(clusteredEdgeId) {
let baseEdgeId = clusteredEdgeId;
let max = 100;
let counter = 0;
while (clusteredEdgeId !== undefined && this.body.edges[clusteredEdgeId] !== undefined && counter < max) {
clusteredEdgeId = this.body.edges[clusteredEdgeId].clusteringEdgeReplacingId;
counter++;
if (clusteredEdgeId !== undefined) {
baseEdgeId = clusteredEdgeId;
}
}
return baseEdgeId;
}
/** /**
* Get the Id the node is connected to * Get the Id the node is connected to

Loading…
Cancel
Save