From 822243e02a7b40dd95304986d7eb5d3e1b8414a5 Mon Sep 17 00:00:00 2001 From: Alex de Mulder Date: Tue, 23 Feb 2016 14:27:32 +0100 Subject: [PATCH] fixed another bug in hierarchical layout --- dist/vis.js | 168 +++++++------- lib/network/modules/LayoutEngine.js | 161 +++++++------ test/networkTest.html | 344 ++++++++++++---------------- 3 files changed, 317 insertions(+), 356 deletions(-) diff --git a/dist/vis.js b/dist/vis.js index 7f1769af..0a083a9a 100644 --- a/dist/vis.js +++ b/dist/vis.js @@ -5,7 +5,7 @@ * A dynamic, browser-based visualization library. * * @version 4.14.0 - * @date 2016-02-21 + * @date 2016-02-23 * * @license * Copyright (C) 2011-2016 Almende B.V, http://almende.com @@ -41012,9 +41012,7 @@ return /******/ (function(modules) { // webpackBootstrap pos = this._getPositionForHierarchy(nodeArray[i - 1]) + this.options.hierarchical.nodeSpacing; } this._setPositionForHierarchy(node, pos, level); - - this.positionedNodes[node.id] = true; - this._placeBranchNodes(node.id, level); + this._validataPositionAndContinue(node, level, pos); handledNodeCount++; } @@ -41023,6 +41021,93 @@ return /******/ (function(modules) { // webpackBootstrap } } + /** + * This is a recursively called function to enumerate the branches from the largest hubs and place the nodes + * on a X position that ensures there will be no overlap. + * + * @param parentId + * @param parentLevel + * @private + */ + }, { + key: '_placeBranchNodes', + value: function _placeBranchNodes(parentId, parentLevel) { + // if this is not a parent, cancel the placing. This can happen with multiple parents to one child. + if (this.hierarchicalChildrenReference[parentId] === undefined) { + return; + } + + // get a list of childNodes + var childNodes = []; + for (var i = 0; i < this.hierarchicalChildrenReference[parentId].length; i++) { + childNodes.push(this.body.nodes[this.hierarchicalChildrenReference[parentId][i]]); + } + + // use the positions to order the nodes. + this._sortNodeArray(childNodes); + + // position the childNodes + for (var i = 0; i < childNodes.length; i++) { + var childNode = childNodes[i]; + var childNodeLevel = this.hierarchicalLevels[childNode.id]; + // check if the child node is below the parent node and if it has already been positioned. + if (childNodeLevel > parentLevel && this.positionedNodes[childNode.id] === undefined) { + // get the amount of space required for this node. If parent the width is based on the amount of children. + var pos = undefined; + + // we get the X or Y values we need and store them in pos and previousPos. The get and set make sure we get X or Y + if (i === 0) { + pos = this._getPositionForHierarchy(this.body.nodes[parentId]); + } else { + pos = this._getPositionForHierarchy(childNodes[i - 1]) + this.options.hierarchical.nodeSpacing; + } + this._setPositionForHierarchy(childNode, pos, childNodeLevel); + this._validataPositionAndContinue(childNode, childNodeLevel, pos); + } else { + return; + } + } + + // center the parent nodes. + var minPos = 1e9; + var maxPos = -1e9; + for (var i = 0; i < childNodes.length; i++) { + var childNodeId = childNodes[i].id; + minPos = Math.min(minPos, this._getPositionForHierarchy(this.body.nodes[childNodeId])); + maxPos = Math.max(maxPos, this._getPositionForHierarchy(this.body.nodes[childNodeId])); + } + this._setPositionForHierarchy(this.body.nodes[parentId], 0.5 * (minPos + maxPos), parentLevel); + } + + /** + * This method checks for overlap and if required shifts the branch. It also keeps records of positioned nodes. + * Finally it will call _placeBranchNodes to place the branch nodes. + * @param node + * @param level + * @param pos + * @private + */ + }, { + key: '_validataPositionAndContinue', + value: function _validataPositionAndContinue(node, level, pos) { + // if overlap has been detected, we shift the branch + if (this.lastNodeOnLevel[level] !== undefined) { + var previousPos = this._getPositionForHierarchy(this.body.nodes[this.lastNodeOnLevel[level]]); + if (pos - previousPos < this.options.hierarchical.nodeSpacing) { + var diff = previousPos + this.options.hierarchical.nodeSpacing - pos; + var sharedParent = this._findCommonParent(this.lastNodeOnLevel[level], node.id); + this._shiftBlock(sharedParent.withChild, diff); + } + } + + // store change in position. + this.lastNodeOnLevel[level] = node.id; + + this.positionedNodes[node.id] = true; + + this._placeBranchNodes(node.id, level); + } + /** * Receives an array with node indices and returns an array with the actual node references. Used for sorting based on * node properties. @@ -41308,80 +41393,6 @@ return /******/ (function(modules) { // webpackBootstrap } } - /** - * This is a recursively called function to enumerate the branches from the largest hubs and place the nodes - * on a X position that ensures there will be no overlap. - * - * @param parentId - * @param parentLevel - * @private - */ - }, { - key: '_placeBranchNodes', - value: function _placeBranchNodes(parentId, parentLevel) { - // if this is not a parent, cancel the placing. This can happen with multiple parents to one child. - if (this.hierarchicalChildrenReference[parentId] === undefined) { - return; - } - - // get a list of childNodes - var childNodes = []; - for (var i = 0; i < this.hierarchicalChildrenReference[parentId].length; i++) { - childNodes.push(this.body.nodes[this.hierarchicalChildrenReference[parentId][i]]); - } - - // use the positions to order the nodes. - this._sortNodeArray(childNodes); - - // position the childNodes - for (var i = 0; i < childNodes.length; i++) { - var childNode = childNodes[i]; - var childNodeLevel = this.hierarchicalLevels[childNode.id]; - // check if the child node is below the parent node and if it has already been positioned. - if (childNodeLevel > parentLevel && this.positionedNodes[childNode.id] === undefined) { - // get the amount of space required for this node. If parent the width is based on the amount of children. - var pos = undefined; - - // we get the X or Y values we need and store them in pos and previousPos. The get and set make sure we get X or Y - if (i === 0) { - pos = this._getPositionForHierarchy(this.body.nodes[parentId]); - } else { - pos = this._getPositionForHierarchy(childNodes[i - 1]) + this.options.hierarchical.nodeSpacing; - } - this._setPositionForHierarchy(childNode, pos, childNodeLevel); - - // if overlap has been detected, we shift the branch - if (this.lastNodeOnLevel[childNodeLevel] !== undefined) { - var previousPos = this._getPositionForHierarchy(this.body.nodes[this.lastNodeOnLevel[childNodeLevel]]); - if (pos - previousPos < this.options.hierarchical.nodeSpacing) { - var diff = previousPos + this.options.hierarchical.nodeSpacing - pos; - var sharedParent = this._findCommonParent(this.lastNodeOnLevel[childNodeLevel], childNode.id); - this._shiftBlock(sharedParent.withChild, diff); - } - } - - // store change in position. - this.lastNodeOnLevel[childNodeLevel] = childNode.id; - - this.positionedNodes[childNode.id] = true; - - this._placeBranchNodes(childNode.id, childNodeLevel); - } else { - return; - } - } - - // center the parent nodes. - var minPos = 1e9; - var maxPos = -1e9; - for (var i = 0; i < childNodes.length; i++) { - var childNodeId = childNodes[i].id; - minPos = Math.min(minPos, this._getPositionForHierarchy(this.body.nodes[childNodeId])); - maxPos = Math.max(maxPos, this._getPositionForHierarchy(this.body.nodes[childNodeId])); - } - this._setPositionForHierarchy(this.body.nodes[parentId], 0.5 * (minPos + maxPos), parentLevel); - } - /** * Shift a branch a certain distance * @param parentId @@ -41457,6 +41468,7 @@ return /******/ (function(modules) { // webpackBootstrap value: function _setPositionForHierarchy(node, position, level) { var doNotUpdate = arguments.length <= 3 || arguments[3] === undefined ? false : arguments[3]; + //console.log('_setPositionForHierarchy',node.id, position) if (doNotUpdate !== true) { if (this.distributionOrdering[level] === undefined) { this.distributionOrdering[level] = []; diff --git a/lib/network/modules/LayoutEngine.js b/lib/network/modules/LayoutEngine.js index 48a6aa98..78ab7f79 100644 --- a/lib/network/modules/LayoutEngine.js +++ b/lib/network/modules/LayoutEngine.js @@ -867,9 +867,7 @@ class LayoutEngine { // we get the X or Y values we need and store them in pos and previousPos. The get and set make sure we get X or Y if (handledNodeCount > 0) {pos = this._getPositionForHierarchy(nodeArray[i-1]) + this.options.hierarchical.nodeSpacing;} this._setPositionForHierarchy(node, pos, level); - - this.positionedNodes[node.id] = true; - this._placeBranchNodes(node.id, level); + this._validataPositionAndContinue(node, level, pos); handledNodeCount++; } @@ -878,6 +876,89 @@ class LayoutEngine { } } + + /** + * This is a recursively called function to enumerate the branches from the largest hubs and place the nodes + * on a X position that ensures there will be no overlap. + * + * @param parentId + * @param parentLevel + * @private + */ + _placeBranchNodes(parentId, parentLevel) { + // if this is not a parent, cancel the placing. This can happen with multiple parents to one child. + if (this.hierarchicalChildrenReference[parentId] === undefined) { + return; + } + + // get a list of childNodes + let childNodes = []; + for (let i = 0; i < this.hierarchicalChildrenReference[parentId].length; i++) { + childNodes.push(this.body.nodes[this.hierarchicalChildrenReference[parentId][i]]); + } + + // use the positions to order the nodes. + this._sortNodeArray(childNodes); + + // position the childNodes + for (let i = 0; i < childNodes.length; i++) { + let childNode = childNodes[i]; + let childNodeLevel = this.hierarchicalLevels[childNode.id]; + // check if the child node is below the parent node and if it has already been positioned. + if (childNodeLevel > parentLevel && this.positionedNodes[childNode.id] === undefined) { + // get the amount of space required for this node. If parent the width is based on the amount of children. + let pos; + + // we get the X or Y values we need and store them in pos and previousPos. The get and set make sure we get X or Y + if (i === 0) {pos = this._getPositionForHierarchy(this.body.nodes[parentId]);} + else {pos = this._getPositionForHierarchy(childNodes[i-1]) + this.options.hierarchical.nodeSpacing;} + this._setPositionForHierarchy(childNode, pos, childNodeLevel); + this._validataPositionAndContinue(childNode, childNodeLevel, pos); + } + else { + return; + } + } + + // center the parent nodes. + let minPos = 1e9; + let maxPos = -1e9; + for (let i = 0; i < childNodes.length; i++) { + let childNodeId = childNodes[i].id; + minPos = Math.min(minPos, this._getPositionForHierarchy(this.body.nodes[childNodeId])); + maxPos = Math.max(maxPos, this._getPositionForHierarchy(this.body.nodes[childNodeId])); + } + this._setPositionForHierarchy(this.body.nodes[parentId], 0.5 * (minPos + maxPos), parentLevel); + } + + + /** + * This method checks for overlap and if required shifts the branch. It also keeps records of positioned nodes. + * Finally it will call _placeBranchNodes to place the branch nodes. + * @param node + * @param level + * @param pos + * @private + */ + _validataPositionAndContinue(node, level, pos) { + // if overlap has been detected, we shift the branch + if (this.lastNodeOnLevel[level] !== undefined) { + let previousPos = this._getPositionForHierarchy(this.body.nodes[this.lastNodeOnLevel[level]]); + if (pos - previousPos < this.options.hierarchical.nodeSpacing) { + let diff = (previousPos + this.options.hierarchical.nodeSpacing) - pos; + let sharedParent = this._findCommonParent(this.lastNodeOnLevel[level], node.id); + this._shiftBlock(sharedParent.withChild, diff); + } + } + + // store change in position. + this.lastNodeOnLevel[level] = node.id; + + this.positionedNodes[node.id] = true; + + this._placeBranchNodes(node.id, level); + } + /** * Receives an array with node indices and returns an array with the actual node references. Used for sorting based on * node properties. @@ -1146,79 +1227,6 @@ class LayoutEngine { } - /** - * This is a recursively called function to enumerate the branches from the largest hubs and place the nodes - * on a X position that ensures there will be no overlap. - * - * @param parentId - * @param parentLevel - * @private - */ - _placeBranchNodes(parentId, parentLevel) { - // if this is not a parent, cancel the placing. This can happen with multiple parents to one child. - if (this.hierarchicalChildrenReference[parentId] === undefined) { - return; - } - - // get a list of childNodes - let childNodes = []; - for (let i = 0; i < this.hierarchicalChildrenReference[parentId].length; i++) { - childNodes.push(this.body.nodes[this.hierarchicalChildrenReference[parentId][i]]); - } - - // use the positions to order the nodes. - this._sortNodeArray(childNodes); - - // position the childNodes - for (let i = 0; i < childNodes.length; i++) { - let childNode = childNodes[i]; - let childNodeLevel = this.hierarchicalLevels[childNode.id]; - // check if the child node is below the parent node and if it has already been positioned. - if (childNodeLevel > parentLevel && this.positionedNodes[childNode.id] === undefined) { - // get the amount of space required for this node. If parent the width is based on the amount of children. - let pos; - - // we get the X or Y values we need and store them in pos and previousPos. The get and set make sure we get X or Y - if (i === 0) {pos = this._getPositionForHierarchy(this.body.nodes[parentId]);} - else {pos = this._getPositionForHierarchy(childNodes[i-1]) + this.options.hierarchical.nodeSpacing;} - this._setPositionForHierarchy(childNode, pos, childNodeLevel); - - // if overlap has been detected, we shift the branch - if (this.lastNodeOnLevel[childNodeLevel] !== undefined) { - let previousPos = this._getPositionForHierarchy(this.body.nodes[this.lastNodeOnLevel[childNodeLevel]]); - if (pos - previousPos < this.options.hierarchical.nodeSpacing) { - let diff = (previousPos + this.options.hierarchical.nodeSpacing) - pos; - let sharedParent = this._findCommonParent(this.lastNodeOnLevel[childNodeLevel], childNode.id); - this._shiftBlock(sharedParent.withChild, diff); - } - } - - // store change in position. - this.lastNodeOnLevel[childNodeLevel] = childNode.id; - - this.positionedNodes[childNode.id] = true; - - this._placeBranchNodes(childNode.id, childNodeLevel); - } - else { - return; - } - } - - // center the parent nodes. - let minPos = 1e9; - let maxPos = -1e9; - for (let i = 0; i < childNodes.length; i++) { - let childNodeId = childNodes[i].id; - minPos = Math.min(minPos, this._getPositionForHierarchy(this.body.nodes[childNodeId])); - maxPos = Math.max(maxPos, this._getPositionForHierarchy(this.body.nodes[childNodeId])); - } - this._setPositionForHierarchy(this.body.nodes[parentId], 0.5 * (minPos + maxPos), parentLevel); - } - - - - /** * Shift a branch a certain distance * @param parentId @@ -1286,6 +1294,7 @@ class LayoutEngine { * @private */ _setPositionForHierarchy(node, position, level, doNotUpdate = false) { + //console.log('_setPositionForHierarchy',node.id, position) if (doNotUpdate !== true) { if (this.distributionOrdering[level] === undefined) { this.distributionOrdering[level] = []; diff --git a/test/networkTest.html b/test/networkTest.html index d4f3d0ef..71208a78 100644 --- a/test/networkTest.html +++ b/test/networkTest.html @@ -7,7 +7,7 @@