vis.js is a dynamic, browser-based visualization library
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

739 lines
25 KiB

9 years ago
9 years ago
  1. let util = require("../../util");
  2. import Cluster from './components/nodes/Cluster'
  3. class ClusterEngine {
  4. constructor(body) {
  5. this.body = body;
  6. this.clusteredNodes = {};
  7. this.options = {};
  8. this.defaultOptions = {};
  9. util.extend(this.options, this.defaultOptions);
  10. this.body.emitter.on('_resetData', () => {this.clusteredNodes = {};})
  11. }
  12. setOptions(options) {
  13. if (options !== undefined) {
  14. }
  15. }
  16. /**
  17. *
  18. * @param hubsize
  19. * @param options
  20. */
  21. clusterByHubsize(hubsize, options) {
  22. if (hubsize === undefined) {
  23. hubsize = this._getHubSize();
  24. }
  25. else if (typeof(hubsize) === "object") {
  26. options = this._checkOptions(hubsize);
  27. hubsize = this._getHubSize();
  28. }
  29. let nodesToCluster = [];
  30. for (let i = 0; i < this.body.nodeIndices.length; i++) {
  31. let node = this.body.nodes[this.body.nodeIndices[i]];
  32. if (node.edges.length >= hubsize) {
  33. nodesToCluster.push(node.id);
  34. }
  35. }
  36. for (let i = 0; i < nodesToCluster.length; i++) {
  37. this.clusterByConnection(nodesToCluster[i],options,false);
  38. }
  39. this.body.emitter.emit('_dataChanged');
  40. }
  41. /**
  42. * loop over all nodes, check if they adhere to the condition and cluster if needed.
  43. * @param options
  44. * @param refreshData
  45. */
  46. cluster(options = {}, refreshData = true) {
  47. if (options.joinCondition === undefined) {throw new Error("Cannot call clusterByNodeData without a joinCondition function in the options.");}
  48. // check if the options object is fine, append if needed
  49. options = this._checkOptions(options);
  50. let childNodesObj = {};
  51. let childEdgesObj = {};
  52. // collect the nodes that will be in the cluster
  53. for (let i = 0; i < this.body.nodeIndices.length; i++) {
  54. let nodeId = this.body.nodeIndices[i];
  55. let node = this.body.nodes[nodeId];
  56. let clonedOptions = this._cloneOptions(node);
  57. if (options.joinCondition(clonedOptions) === true) {
  58. childNodesObj[nodeId] = this.body.nodes[nodeId];
  59. // collect the nodes that will be in the cluster
  60. for (let i = 0; i < node.edges.length; i++) {
  61. let edge = node.edges[i];
  62. childEdgesObj[edge.id] = edge;
  63. }
  64. }
  65. }
  66. this._cluster(childNodesObj, childEdgesObj, options, refreshData);
  67. }
  68. /**
  69. * Cluster all nodes in the network that have only X edges
  70. * @param edgeCount
  71. * @param options
  72. * @param refreshData
  73. */
  74. clusterByEdgeCount(edgeCount, options, refreshData = true) {
  75. options = this._checkOptions(options);
  76. let clusters = [];
  77. // collect the nodes that will be in the cluster
  78. for (let i = 0; i < this.body.nodeIndices.length; i++) {
  79. let childNodesObj = {};
  80. let childEdgesObj = {};
  81. let nodeId = this.body.nodeIndices[i];
  82. let visibleEdges = 0;
  83. let edge;
  84. for (let j = 0; j < this.body.nodes[nodeId].edges.length; j++) {
  85. if (this.body.nodes[nodeId].edges[j].options.hidden === false) {
  86. visibleEdges++;
  87. edge = this.body.nodes[nodeId].edges[j];
  88. }
  89. }
  90. if (visibleEdges === edgeCount) {
  91. // this is a qualifying node
  92. let childNodeId = this._getConnectedId(edge, nodeId);
  93. if (childNodeId !== nodeId) {
  94. if (options.joinCondition === undefined) {
  95. if (this._checkIfUsed(clusters,nodeId,edge.id) === false && this._checkIfUsed(clusters,childNodeId,edge.id) === false) {
  96. childEdgesObj[edge.id] = edge;
  97. childNodesObj[nodeId] = this.body.nodes[nodeId];
  98. childNodesObj[childNodeId] = this.body.nodes[childNodeId];
  99. }
  100. }
  101. else {
  102. let clonedOptions = this._cloneOptions(this.body.nodes[nodeId]);
  103. if (options.joinCondition(clonedOptions) === true && this._checkIfUsed(clusters,nodeId,edge.id) === false) {
  104. childEdgesObj[edge.id] = edge;
  105. childNodesObj[nodeId] = this.body.nodes[nodeId];
  106. }
  107. clonedOptions = this._cloneOptions(this.body.nodes[childNodeId]);
  108. if (options.joinCondition(clonedOptions) === true && this._checkIfUsed(clusters,nodeId,edge.id) === false) {
  109. childEdgesObj[edge.id] = edge;
  110. childNodesObj[childNodeId] = this.body.nodes[childNodeId];
  111. }
  112. }
  113. if (Object.keys(childNodesObj).length > 0 && Object.keys(childEdgesObj).length > 0) {
  114. clusters.push({nodes: childNodesObj, edges: childEdgesObj})
  115. }
  116. }
  117. }
  118. }
  119. for (let i = 0; i < clusters.length; i++) {
  120. this._cluster(clusters[i].nodes, clusters[i].edges, options, false)
  121. }
  122. if (refreshData === true) {
  123. this.body.emitter.emit('_dataChanged');
  124. }
  125. }
  126. /**
  127. * Cluster all nodes in the network that have only 1 edge
  128. * @param options
  129. * @param refreshData
  130. */
  131. clusterOutliers(options, refreshData = true) {
  132. this.clusterByEdgeCount(1,options,refreshData);
  133. }
  134. /**
  135. * Cluster all nodes in the network that have only 2 edge
  136. * @param options
  137. * @param refreshData
  138. */
  139. clusterBridges(options, refreshData = true) {
  140. this.clusterByEdgeCount(2,options,refreshData);
  141. }
  142. _checkIfUsed(clusters, nodeId, edgeId) {
  143. for (let i = 0; i < clusters.length; i++) {
  144. let cluster = clusters[i];
  145. if (cluster.nodes[nodeId] !== undefined || cluster.edges[edgeId] !== undefined) {
  146. return true;
  147. }
  148. }
  149. return false;
  150. }
  151. /**
  152. * suck all connected nodes of a node into the node.
  153. * @param nodeId
  154. * @param options
  155. * @param refreshData
  156. */
  157. clusterByConnection(nodeId, options, refreshData = true) {
  158. // kill conditions
  159. if (nodeId === undefined) {throw new Error("No nodeId supplied to clusterByConnection!");}
  160. if (this.body.nodes[nodeId] === undefined) {throw new Error("The nodeId given to clusterByConnection does not exist!");}
  161. let node = this.body.nodes[nodeId];
  162. options = this._checkOptions(options, node);
  163. if (options.clusterNodeProperties.x === undefined) {options.clusterNodeProperties.x = node.x;}
  164. if (options.clusterNodeProperties.y === undefined) {options.clusterNodeProperties.y = node.y;}
  165. if (options.clusterNodeProperties.fixed === undefined) {
  166. options.clusterNodeProperties.fixed = {};
  167. options.clusterNodeProperties.fixed.x = node.options.fixed.x;
  168. options.clusterNodeProperties.fixed.y = node.options.fixed.y;
  169. }
  170. let childNodesObj = {};
  171. let childEdgesObj = {};
  172. let parentNodeId = node.id;
  173. let parentClonedOptions = this._cloneOptions(node);
  174. childNodesObj[parentNodeId] = node;
  175. // collect the nodes that will be in the cluster
  176. for (let i = 0; i < node.edges.length; i++) {
  177. let edge = node.edges[i];
  178. let childNodeId = this._getConnectedId(edge, parentNodeId);
  179. if (this.clusteredNodes[childNodeId] === undefined) {
  180. if (childNodeId !== parentNodeId) {
  181. if (options.joinCondition === undefined) {
  182. childEdgesObj[edge.id] = edge;
  183. childNodesObj[childNodeId] = this.body.nodes[childNodeId];
  184. }
  185. else {
  186. // clone the options and insert some additional parameters that could be interesting.
  187. let childClonedOptions = this._cloneOptions(this.body.nodes[childNodeId]);
  188. if (options.joinCondition(parentClonedOptions, childClonedOptions) === true) {
  189. childEdgesObj[edge.id] = edge;
  190. childNodesObj[childNodeId] = this.body.nodes[childNodeId];
  191. }
  192. }
  193. }
  194. else {
  195. childEdgesObj[edge.id] = edge;
  196. }
  197. }
  198. }
  199. this._cluster(childNodesObj, childEdgesObj, options, refreshData);
  200. }
  201. /**
  202. * This returns a clone of the options or options of the edge or node to be used for construction of new edges or check functions for new nodes.
  203. * @param objId
  204. * @param type
  205. * @returns {{}}
  206. * @private
  207. */
  208. _cloneOptions(item, type) {
  209. let clonedOptions = {};
  210. if (type === undefined || type === 'node') {
  211. util.deepExtend(clonedOptions, item.options, true);
  212. clonedOptions.x = item.x;
  213. clonedOptions.y = item.y;
  214. clonedOptions.amountOfConnections = item.edges.length;
  215. }
  216. else {
  217. util.deepExtend(clonedOptions, item.options, true);
  218. }
  219. return clonedOptions;
  220. }
  221. /**
  222. * This function creates the edges that will be attached to the cluster.
  223. *
  224. * @param childNodesObj
  225. * @param childEdgesObj
  226. * @param newEdges
  227. * @param options
  228. * @private
  229. */
  230. _createClusterEdges (childNodesObj, childEdgesObj, newEdges, clusterNodeProperties, clusterEdgeProperties) {
  231. let edge, childNodeId, childNode, toId, fromId, otherNodeId;
  232. let childKeys = Object.keys(childNodesObj);
  233. for (let i = 0; i < childKeys.length; i++) {
  234. childNodeId = childKeys[i];
  235. childNode = childNodesObj[childNodeId];
  236. // construct new edges from the cluster to others
  237. for (let j = 0; j < childNode.edges.length; j++) {
  238. edge = childNode.edges[j];
  239. childEdgesObj[edge.id] = edge;
  240. // childNodeId position will be replaced by the cluster.
  241. if (edge.toId == childNodeId) { // this is a double equals because ints and strings can be interchanged here.
  242. toId = clusterNodeProperties.id;
  243. fromId = edge.fromId;
  244. otherNodeId = fromId;
  245. }
  246. else {
  247. toId = edge.toId;
  248. fromId = clusterNodeProperties.id;
  249. otherNodeId = toId;
  250. }
  251. // if the node connected to the cluster is also in the cluster we do not need a new edge.
  252. if (childNodesObj[otherNodeId] === undefined) {
  253. let clonedOptions = this._cloneOptions(edge, 'edge');
  254. util.deepExtend(clonedOptions, clusterEdgeProperties);
  255. clonedOptions.from = fromId;
  256. clonedOptions.to = toId;
  257. clonedOptions.id = 'clusterEdge:' + util.randomUUID();
  258. newEdges.push(this.body.functions.createEdge(clonedOptions));
  259. }
  260. }
  261. }
  262. }
  263. /**
  264. * This function checks the options that can be supplied to the different cluster functions
  265. * for certain fields and inserts defaults if needed
  266. * @param options
  267. * @returns {*}
  268. * @private
  269. */
  270. _checkOptions(options = {}) {
  271. if (options.clusterEdgeProperties === undefined) {options.clusterEdgeProperties = {};}
  272. if (options.clusterNodeProperties === undefined) {options.clusterNodeProperties = {};}
  273. return options;
  274. }
  275. /**
  276. *
  277. * @param {Object} childNodesObj | object with node objects, id as keys, same as childNodes except it also contains a source node
  278. * @param {Object} childEdgesObj | object with edge objects, id as keys
  279. * @param {Array} options | object with {clusterNodeProperties, clusterEdgeProperties, processProperties}
  280. * @param {Boolean} refreshData | when true, do not wrap up
  281. * @private
  282. */
  283. _cluster(childNodesObj, childEdgesObj, options, refreshData = true) {
  284. // kill condition: no children so can't cluster
  285. if (Object.keys(childNodesObj).length === 0) {return;}
  286. // check if this cluster call is not trying to cluster anything that is in another cluster.
  287. for (let nodeId in childNodesObj) {
  288. if (childNodesObj.hasOwnProperty(nodeId)) {
  289. if (this.clusteredNodes[nodeId] !== undefined) {
  290. return;
  291. }
  292. }
  293. }
  294. let clusterNodeProperties = util.deepExtend({},options.clusterNodeProperties);
  295. // construct the clusterNodeProperties
  296. if (options.processProperties !== undefined) {
  297. // get the childNode options
  298. let childNodesOptions = [];
  299. for (let nodeId in childNodesObj) {
  300. if (childNodesObj.hasOwnProperty(nodeId)) {
  301. let clonedOptions = this._cloneOptions(childNodesObj[nodeId]);
  302. childNodesOptions.push(clonedOptions);
  303. }
  304. }
  305. // get clusterproperties based on childNodes
  306. let childEdgesOptions = [];
  307. for (let edgeId in childEdgesObj) {
  308. if (childEdgesObj.hasOwnProperty(edgeId)) {
  309. // these cluster edges will be removed on creation of the cluster.
  310. if (edgeId.substr(0, 12) !== "clusterEdge:") {
  311. let clonedOptions = this._cloneOptions(childEdgesObj[edgeId], 'edge');
  312. childEdgesOptions.push(clonedOptions);
  313. }
  314. }
  315. }
  316. clusterNodeProperties = options.processProperties(clusterNodeProperties, childNodesOptions, childEdgesOptions);
  317. if (!clusterNodeProperties) {
  318. throw new Error("The processProperties function does not return properties!");
  319. }
  320. }
  321. // check if we have an unique id;
  322. if (clusterNodeProperties.id === undefined) {clusterNodeProperties.id = 'cluster:' + util.randomUUID();}
  323. let clusterId = clusterNodeProperties.id;
  324. if (clusterNodeProperties.label === undefined) {
  325. clusterNodeProperties.label = 'cluster';
  326. }
  327. // give the clusterNode a postion if it does not have one.
  328. let pos = undefined;
  329. if (clusterNodeProperties.x === undefined) {
  330. pos = this._getClusterPosition(childNodesObj);
  331. clusterNodeProperties.x = pos.x;
  332. }
  333. if (clusterNodeProperties.y === undefined) {
  334. if (pos === undefined) {
  335. pos = this._getClusterPosition(childNodesObj);
  336. }
  337. clusterNodeProperties.y = pos.y;
  338. }
  339. // force the ID to remain the same
  340. clusterNodeProperties.id = clusterId;
  341. // create the clusterNode
  342. let clusterNode = this.body.functions.createNode(clusterNodeProperties, Cluster);
  343. clusterNode.isCluster = true;
  344. clusterNode.containedNodes = childNodesObj;
  345. clusterNode.containedEdges = childEdgesObj;
  346. // cache a copy from the cluster edge properties if we have to reconnect others later on
  347. clusterNode.clusterEdgeProperties = options.clusterEdgeProperties;
  348. // finally put the cluster node into global
  349. this.body.nodes[clusterNodeProperties.id] = clusterNode;
  350. // create the new edges that will connect to the cluster
  351. let newEdges = [];
  352. this._createClusterEdges(childNodesObj, childEdgesObj, newEdges, clusterNodeProperties, options.clusterEdgeProperties);
  353. // disable the childEdges
  354. for (let edgeId in childEdgesObj) {
  355. if (childEdgesObj.hasOwnProperty(edgeId)) {
  356. if (this.body.edges[edgeId] !== undefined) {
  357. let edge = this.body.edges[edgeId];
  358. // if this is a cluster edge that is fully encompassed in the cluster, we want to delete it
  359. // this check verifies that both of the connected nodes are in this cluster
  360. if (edgeId.substr(0,12) === "clusterEdge:" && childNodesObj[edge.fromId] !== undefined && childNodesObj[edge.toId] !== undefined) {
  361. edge.cleanup();
  362. // this removes the edge from node.edges, which is why edgeIds is formed
  363. edge.disconnect();
  364. delete childEdgesObj[edgeId];
  365. delete this.body.edges[edgeId];
  366. }
  367. else {
  368. edge.setOptions({physics:false, hidden:true});
  369. //edge.options.hidden = true;
  370. }
  371. }
  372. }
  373. }
  374. // disable the childNodes
  375. for (let nodeId in childNodesObj) {
  376. if (childNodesObj.hasOwnProperty(nodeId)) {
  377. this.clusteredNodes[nodeId] = {clusterId:clusterNodeProperties.id, node: this.body.nodes[nodeId]};
  378. this.body.nodes[nodeId].setOptions({hidden:true, physics:false});
  379. }
  380. }
  381. // push new edges to global
  382. for (let i = 0; i < newEdges.length; i++) {
  383. this.body.edges[newEdges[i].id] = newEdges[i];
  384. this.body.edges[newEdges[i].id].connect();
  385. }
  386. // set ID to undefined so no duplicates arise
  387. clusterNodeProperties.id = undefined;
  388. // wrap up
  389. if (refreshData === true) {
  390. this.body.emitter.emit('_dataChanged');
  391. }
  392. }
  393. /**
  394. * Check if a node is a cluster.
  395. * @param nodeId
  396. * @returns {*}
  397. */
  398. isCluster(nodeId) {
  399. if (this.body.nodes[nodeId] !== undefined) {
  400. return this.body.nodes[nodeId].isCluster === true;
  401. }
  402. else {
  403. console.log("Node does not exist.");
  404. return false;
  405. }
  406. }
  407. /**
  408. * get the position of the cluster node based on what's inside
  409. * @param {object} childNodesObj | object with node objects, id as keys
  410. * @returns {{x: number, y: number}}
  411. * @private
  412. */
  413. _getClusterPosition(childNodesObj) {
  414. let childKeys = Object.keys(childNodesObj);
  415. let minX = childNodesObj[childKeys[0]].x;
  416. let maxX = childNodesObj[childKeys[0]].x;
  417. let minY = childNodesObj[childKeys[0]].y;
  418. let maxY = childNodesObj[childKeys[0]].y;
  419. let node;
  420. for (let i = 1; i < childKeys.length; i++) {
  421. node = childNodesObj[childKeys[i]];
  422. minX = node.x < minX ? node.x : minX;
  423. maxX = node.x > maxX ? node.x : maxX;
  424. minY = node.y < minY ? node.y : minY;
  425. maxY = node.y > maxY ? node.y : maxY;
  426. }
  427. return {x: 0.5*(minX + maxX), y: 0.5*(minY + maxY)};
  428. }
  429. /**
  430. * Open a cluster by calling this function.
  431. * @param {String} clusterNodeId | the ID of the cluster node
  432. * @param {Boolean} refreshData | wrap up afterwards if not true
  433. */
  434. openCluster(clusterNodeId, options, refreshData = true) {
  435. // kill conditions
  436. if (clusterNodeId === undefined) {throw new Error("No clusterNodeId supplied to openCluster.");}
  437. if (this.body.nodes[clusterNodeId] === undefined) {throw new Error("The clusterNodeId supplied to openCluster does not exist.");}
  438. if (this.body.nodes[clusterNodeId].containedNodes === undefined) {
  439. console.log("The node:" + clusterNodeId + " is not a cluster.");
  440. return
  441. }
  442. let clusterNode = this.body.nodes[clusterNodeId];
  443. let containedNodes = clusterNode.containedNodes;
  444. let containedEdges = clusterNode.containedEdges;
  445. // allow the user to position the nodes after release.
  446. if (options !== undefined && options.releaseFunction !== undefined && typeof options.releaseFunction === 'function') {
  447. let positions = {};
  448. let clusterPosition = {x:clusterNode.x, y:clusterNode.y};
  449. for (let nodeId in containedNodes) {
  450. if (containedNodes.hasOwnProperty(nodeId)) {
  451. let containedNode = this.body.nodes[nodeId];
  452. positions[nodeId] = {x: containedNode.x, y: containedNode.y};
  453. }
  454. }
  455. let newPositions = options.releaseFunction(clusterPosition, positions);
  456. for (let nodeId in containedNodes) {
  457. if (containedNodes.hasOwnProperty(nodeId)) {
  458. let containedNode = this.body.nodes[nodeId];
  459. if (newPositions[nodeId] !== undefined) {
  460. containedNode.x = newPositions[nodeId].x || clusterNode.x;
  461. containedNode.y = newPositions[nodeId].y || clusterNode.y;
  462. }
  463. }
  464. }
  465. }
  466. else {
  467. // copy the position from the cluster
  468. for (let nodeId in containedNodes) {
  469. if (containedNodes.hasOwnProperty(nodeId)) {
  470. let containedNode = this.body.nodes[nodeId];
  471. containedNode = containedNodes[nodeId];
  472. // inherit position
  473. containedNode.x = clusterNode.x;
  474. containedNode.y = clusterNode.y;
  475. }
  476. }
  477. }
  478. // release nodes
  479. for (let nodeId in containedNodes) {
  480. if (containedNodes.hasOwnProperty(nodeId)) {
  481. let containedNode = this.body.nodes[nodeId];
  482. // inherit speed
  483. containedNode.vx = clusterNode.vx;
  484. containedNode.vy = clusterNode.vy;
  485. // we use these methods to avoid reinstantiating the shape, which happens with setOptions.
  486. //containedNode.toggleHidden(false);
  487. //containedNode.togglePhysics(true);
  488. containedNode.setOptions({hidden:false, physics:true});
  489. delete this.clusteredNodes[nodeId];
  490. }
  491. }
  492. // release edges
  493. for (let edgeId in containedEdges) {
  494. if (containedEdges.hasOwnProperty(edgeId)) {
  495. let edge = containedEdges[edgeId];
  496. // if this edge was a temporary edge and it's connected nodes do not exist anymore, we remove it from the data
  497. if (this.body.nodes[edge.fromId] === undefined || this.body.nodes[edge.toId] === undefined || edge.toId == clusterNodeId || edge.fromId == clusterNodeId) {
  498. edge.cleanup();
  499. // this removes the edge from node.edges, which is why edgeIds is formed
  500. edge.disconnect();
  501. delete this.body.edges[edgeId];
  502. }
  503. else {
  504. // one of the nodes connected to this edge is in a cluster. We give the edge to that cluster so it will be released when that cluster is opened.
  505. if (this.clusteredNodes[edge.fromId] !== undefined || this.clusteredNodes[edge.toId] !== undefined) {
  506. let fromId, toId;
  507. let clusteredNode = this.clusteredNodes[edge.fromId] || this.clusteredNodes[edge.toId];
  508. let clusterId = clusteredNode.clusterId;
  509. let clusterNode = this.body.nodes[clusterId];
  510. clusterNode.containedEdges[edgeId] = edge;
  511. if (this.clusteredNodes[edge.fromId] !== undefined) {
  512. fromId = clusterId;
  513. toId = edge.toId;
  514. }
  515. else {
  516. fromId = edge.fromId;
  517. toId = clusterId;
  518. }
  519. // if both from and to nodes are visible, we create a new temporary edge
  520. if (this.body.nodes[fromId].options.hidden !== true && this.body.nodes[toId].options.hidden !== true) {
  521. let clonedOptions = this._cloneOptions(edge, 'edge');
  522. let id = 'clusterEdge:' + util.randomUUID();
  523. util.deepExtend(clonedOptions, clusterNode.clusterEdgeProperties);
  524. util.deepExtend(clonedOptions, {from: fromId, to: toId, hidden: false, physics: true, id: id});
  525. let newEdge = this.body.functions.createEdge(clonedOptions);
  526. this.body.edges[id] = newEdge;
  527. this.body.edges[id].connect();
  528. }
  529. }
  530. else {
  531. edge.setOptions({physics:true, hidden:false});
  532. //edge.options.hidden = false;
  533. //edge.togglePhysics(true);
  534. }
  535. }
  536. }
  537. }
  538. // remove all temporary edges, make an array of ids so we don't remove from the list we're iterating over.
  539. let removeIds = [];
  540. for (let i = 0; i < clusterNode.edges.length; i++) {
  541. let edgeId = clusterNode.edges[i].id;
  542. removeIds.push(edgeId);
  543. }
  544. // actually removing the edges
  545. for (let i = 0; i < removeIds.length; i++) {
  546. let edgeId = removeIds[i];
  547. this.body.edges[edgeId].cleanup();
  548. // this removes the edge from node.edges, which is why edgeIds is formed
  549. this.body.edges[edgeId].disconnect();
  550. delete this.body.edges[edgeId];
  551. }
  552. // remove clusterNode
  553. delete this.body.nodes[clusterNodeId];
  554. if (refreshData === true) {
  555. this.body.emitter.emit('_dataChanged');
  556. }
  557. }
  558. getNodesInCluster(clusterId) {
  559. let nodesArray = []
  560. if (this.isCluster(clusterId) === true) {
  561. let containedNodes = this.body.nodes[clusterId].containedNodes;
  562. for (let nodeId in containedNodes) {
  563. if (containedNodes.hasOwnProperty(nodeId)) {
  564. nodesArray.push(nodeId)
  565. }
  566. }
  567. }
  568. return nodesArray;
  569. }
  570. /**
  571. * Get the stack clusterId's that a certain node resides in. cluster A -> cluster B -> cluster C -> node
  572. * @param nodeId
  573. * @returns {Array}
  574. * @private
  575. */
  576. findNode(nodeId) {
  577. let stack = [];
  578. let max = 100;
  579. let counter = 0;
  580. while (this.clusteredNodes[nodeId] !== undefined && counter < max) {
  581. stack.push(this.clusteredNodes[nodeId].node);
  582. nodeId = this.clusteredNodes[nodeId].clusterId;
  583. counter++;
  584. }
  585. stack.push(this.body.nodes[nodeId]);
  586. return stack;
  587. }
  588. /**
  589. * Get the Id the node is connected to
  590. * @param edge
  591. * @param nodeId
  592. * @returns {*}
  593. * @private
  594. */
  595. _getConnectedId(edge, nodeId) {
  596. if (edge.toId != nodeId) {
  597. return edge.toId;
  598. }
  599. else if (edge.fromId != nodeId) {
  600. return edge.fromId;
  601. }
  602. else {
  603. return edge.fromId;
  604. }
  605. }
  606. /**
  607. * We determine how many connections denote an important hub.
  608. * We take the mean + 2*std as the important hub size. (Assuming a normal distribution of data, ~2.2%)
  609. *
  610. * @private
  611. */
  612. _getHubSize() {
  613. let average = 0;
  614. let averageSquared = 0;
  615. let hubCounter = 0;
  616. let largestHub = 0;
  617. for (let i = 0; i < this.body.nodeIndices.length; i++) {
  618. let node = this.body.nodes[this.body.nodeIndices[i]];
  619. if (node.edges.length > largestHub) {
  620. largestHub = node.edges.length;
  621. }
  622. average += node.edges.length;
  623. averageSquared += Math.pow(node.edges.length,2);
  624. hubCounter += 1;
  625. }
  626. average = average / hubCounter;
  627. averageSquared = averageSquared / hubCounter;
  628. let variance = averageSquared - Math.pow(average,2);
  629. let standardDeviation = Math.sqrt(variance);
  630. let hubThreshold = Math.floor(average + 2*standardDeviation);
  631. // always have at least one to cluster
  632. if (hubThreshold > largestHub) {
  633. hubThreshold = largestHub;
  634. }
  635. return hubThreshold;
  636. };
  637. }
  638. export default ClusterEngine;