The algorithm used to ascertain the levels of the nodes based on the data. The possible options are: hubsize, directed.
Hubsize takes the nodes with the most edges and puts them at the top. From that the rest of the hierarchy is evaluated.
Directed adheres to the to and from data of the edges. A --> B so B is a level lower than A.
+
+
hierarchical.userControlsFreeAxis
+
Boolean
+
false
+
Whether or not the value specified by a node's Datasetx or y values will affect a node's layout along its "free" axis.
+ If the hierarchical layout direction is either "DU" or "UD", vis.js will layout the node using the Node's x property,
+ or if directionis either "LR" or "RL", vis.js will use the node's y property.
+ If the property vis wants to use (e.g., the x or y property on the node within the target Dataset)
+ is undefined, the default behavior is invoked, as though this option were set to false. This provides the ability to initally utilize Vis's default layout,
+ then later call Network.storePositions() and have the hierarchical layout engine respect the retreived values.
+ This option is helpful because updating a hierarchically layed-out graph will trigger a redraw, and without this option, if a node has been moved along its free axis,
+ it will be returned to its default position.
+ This option is useful with physics disabled.
+
diff --git a/examples/network/other/manipulation.html b/examples/network/other/manipulation.html
index f86b6b90c..5921790a0 100644
--- a/examples/network/other/manipulation.html
+++ b/examples/network/other/manipulation.html
@@ -64,6 +64,97 @@
// randomly create some nodes and edges
var data = getScaleFreeNetwork(25);
var seed = 2;
+
+ function bakeLevels() {
+ let direction = network.layoutEngine.options.hierarchical.direction;
+ let levelAxis = "";
+
+ //
+ // direction can only be "UD" | "DU" | "LR" | "RL", other values are caught as errors earlier in the program
+ // once levels are baked, they should not change even if the user alters the direction of the layout
+ //
+
+ if (direction == "UD" || direction == "DU") {
+ levelAxis = "y";
+ }
+ else {
+ levelAxis = "x";
+ }
+
+ let nodes = network.body.nodes;
+ let nodeIds = Object.getOwnPropertyNames(nodes);
+ let nodeLevelMap = new Map();
+ let uniqueLevelsInCanvasSpace = new Set();
+
+ for (let nodeId of nodeIds) {
+ let node = nodes[nodeId];
+ uniqueLevelsInCanvasSpace.add(node[levelAxis]);
+ if (nodeLevelMap.has(node[levelAxis])) {
+ nodeLevelMap.get(node[levelAxis]).push(node.id);
+ }
+ else {
+ nodeLevelMap.set(node[levelAxis], [node.id]);
+ }
+ }
+
+ let orderedUniqueLevelsInCanvasSpace = Array.from(uniqueLevelsInCanvasSpace);
+ orderedUniqueLevelsInCanvasSpace.sort((a,b) => { return a-b; });
+ canvasLevelMap = new Map();
+
+ for (let i = 0; i < orderedUniqueLevelsInCanvasSpace.length; i++) {
+ canvasLevelMap.set(orderedUniqueLevelsInCanvasSpace[i], i);
+ }
+
+ let updateList = []
+
+ for (let nodeId of nodeIds) {
+ let node = nodes[nodeId];
+ updateList.push({
+ "id": nodeId,
+ "level": canvasLevelMap.get(node[levelAxis])
+ });
+ }
+
+ network.body.data.nodes.update(updateList);
+
+ }
+
+ function storeFreeAxisPositions() {
+ let direction = network.layoutEngine.options.hierarchical.direction;
+ let freeAxis = "";
+
+ if (direction == "UD" || direction == "DU") {
+ freeAxis = "x";
+ }
+ else {
+ freeAxis = "y";
+ }
+
+ let positions = network.getPositions();
+ let keys = Object.getOwnPropertyNames(positions);
+ let updateList = [];
+
+ for (let key of keys) {
+ let pos = positions[key];
+ updateList.push({
+ id: key,
+ [freeAxis]: pos[freeAxis]
+ });
+ }
+
+ network.body.data.nodes.update(updateList);
+ }
+
+ function toggleBakeLevelsButton() {
+ let target = document.getElementById("bake-levels");
+ let isDisabled = target.hasAttribute("disabled");
+ if (isDisabled) {
+ target.removeAttribute("disabled");
+ }
+ else {
+ target.setAttribute("disabled", "")
+ }
+ }
function setDefaultLocale() {
var defaultLocal = navigator.language;
@@ -92,7 +183,10 @@
// create a network
var container = document.getElementById('mynetwork');
var options = {
- layout: {randomSeed:seed}, // just to make sure the layout is the same when the locale is changed
+ configure: true,
+ layout: {
+ randomSeed:seed
+ }, // just to make sure the layout is the same when the locale is changed
locale: document.getElementById('locale').value,
manipulation: {
addNode: function (data, callback) {
@@ -127,6 +221,18 @@
}
};
network = new vis.Network(container, data, options);
+ network.on("configChange", (e) => {
+ if (e.layout) {
+ if (e.layout.hierarchical) {
+ if (e.layout.hierarchical === true || e.layout.hierarchical.enabled === false) {
+ toggleBakeLevelsButton();
+ }
+ }
+ }
+ });
+ network.on("dragEnd", (e) => {
+ storeFreeAxisPositions();
+ });
}
function clearPopUp() {
@@ -187,6 +293,13 @@