I’ trying to build a Petri Net with Cytoscape.js, using Dagre.js layout and compound nodes for representing places witch in turns are subnets.
I’ using Expand-Collapse extension (Dagre layout) , starting with an overall collapsed network. As the net evolve, I need to update data on every node, including children of collapsed nodes witch users may decide to expand or not .
Here is the issue: I’ not able to select nodes inside collapsed ones nor I can test with IsParent() or any other function applying to compound nodes including slector like “node > node”. Any idea ?
Thanks.
You can't change state of elements that aren't in the graph. Modify them after you add them back (.restore()) to the graph instead.
Related
I like to find a node or an element based on coordinates in odb in order to plot the data at that node or element. I am wondering if it is possible to use getByBoundingBox() or findAt() in odb? If so, how does it work in odb to find the node and element based on coordinates? Thanks
Yes. You can use the getByBoundingSphere(...) on any MeshNodeArray object.
As example, a MeshNodeArray can be accessed through the root structure: odb/rootAssembly/instance/elements
I am using vue-cytoscape to render a graph and navigate through a tree-like data structure.
My goal is to expand parent nodes and keep their position in the graph. I would like to simply add the new children nodes.
My approach is to lock current nodes, add the children and unlock the nodes.
this.cy.nodes().lock()
for(let d of data){
this.cy.add(d)
}
this.cy.elements().layout(this.config.layout).run()
setTimeout(() => {this.cy.nodes().unlock()}, 2000) // Give some time for the layout to render before unlocking nodes.
The problem is that the layouts do not consider the locked state of the nodes. Only the new nodes are moved around, which is fine. But the layout is not respected. I am under the impression that the layout calculates a new position for all nodes, but then moves only nodes that are unlocked.
According to this GitHub issue, some layout algorithm should handle locked nodes. I am using the following layouts and none seem to consider locked nodes.
Cola
Fcose
Dagre
avsdf
grid
concentric
Please try calling the layout function only on the added nodes:
var eles = cy.add(data); // refer to http://js.cytoscape.org/#cy.add for adding nodes
eles.layout(this.config.layout).run();
If you don't want nodes to move when calling the layout function, you can exclude them from the rendering. While calling cy.add(), the function returns an object with every added element inside (see var eles = ... in the code).
I'm using cytoscape.js and cytoscape.js-expand-collapse to create dynamic hierarchies in my graph structure. I would like to be able to dynamically create a collapsed(merged) node that could potentially be expanded out, removed or possibly re-merged with additional nodes. I am having trouble whenever I call nodes.move({parent:null}). They become detached from the graph and I cannot re-attach them to a new parentNode. If I call restore on them I will see errors saying they already exist in the graph.
merge and unmerge by itself works fine in a simple case of no existing collapsed nodes. However calling merge on something that already contains a compound collapsed node breaks things. I would get the resulting merged node without the children of the previously collapsed merge candidate
Update #1 I've realized my problem was returning the wrong moved nodes. Calling .move on a collection returns a new set of nodes. so the unmerge should return those instead.
Update #2 cytoscape-expand-collapse utility does some internal book-keeping during the parent node collapse/expand by tucking away node data in 'originalEnds' data prop on the meta edges. so, if I'm now altering the graph dynamically by moving nodes in/out of parents these originalEnds pointers get out of sync causing infinite loops. I am doing some additional node tracking of my own as a workaround.
function merge(nodes){
//if one of the nodes is a compound itself, first uncollapse, then merge
var collapsed = nodes.filter(function(c){return typeof c.data('collapsedChildren')!=='undefined';});
if (collapsed.length>0) {
// for now just assume collapsed is a length of 1
var unmerged = unmerge(collapsed); //unmerged should be now the former collapsed children
nodes = nodes.subtract(collapsed).union(unmerged.nodes());
}
var parentNode = cy.add({group:'nodes', data: {id: parentID}});
nodes.move({parent: parentID});
collapseApi.collapse(parentNode);
return parentNode;
}
function unmerge(parentNode){
collapseApi.expand(parentNode);
var children = parentNode.children();
var moved = children.move({parent:null});
//at this point children become "detached" from the graph
// .removed() returns true, however, calling restore() logs an error and they are still technically in the graph
parentNode.remove();
return moved;
}
ele.move() has a more convenient implementation in cytoscape>=3.4: The elements are modified in-place instead of creating replacement elements.
Old code that uses the returned collection from ele.move() will still work. New code can be simplified by not having to use the returned collections at all.
I was wondering how feasible is to add some compound elements (nodes) "on the go".
Scenario:
I have a network with nodes from n1...n10 and edges. Depending on the button the user clicks, it redraws my network including nodes inside a compound node.
Example:
When you open the graph, you have n1..n10, without compounds, but when you click on a pre-defined button, my new graph now would be:
A new compound node with n1:n5 inside (parent), and the rest n6:n10 would stay the same (outside compound).
How feasible is it ?
I've tried :
cy.batchData({
"5": {
parent: "n0" // new element I added earlier
}});
to update my element id=5 to have n0 as parent, but it haven't worked.
The main idea is to represent data (graph) with biological insight, where the "new compound area" would be a pathway or a metabolic path (or whatever I want to represent there), one by one, so the visualization won't be a mess.
Thank you.
You can't change the parent field; it's immutable. You may have to remove the elements and add the second (resultant) graph via eles.remove() and cy.add() respectively.
I am trying to add a large number of items (100+) to my tree via ForestStoreModel by calling newItem in a loop. This seems to be quite slow and locks up the browser. Is there any way I can do something similar to grid's beginUpdate & endUpdate? I want to basically 'turn off' my tree, add 100 items in a batch, then 'turn on' my tree. Any ideas? Thanks!
As Stephen Chung suggested, the best way would be to only add nodes that the user requires at a specific node level; but sometimes you may have a large number of nodes at the same level or other things might get in the way. In order to update the tree with a large number of items all at once, I would suggest creating a new store/updating your old store, then you need to refresh the tree rendering. So once the store has the correctly formatted array, do this (credit to Layke for the hack How to update dojo tree data dynamically):
// Completely delete every node from the dijit.Tree
myTree._itemNodesMap = {};
myTree.rootNode.state = "UNCHECKED";
myTree.model.root.children = null;
// Destroy the widget
myTree.rootNode.destroyRecursive();
// Recreate the model, (with the model again)
myTree.model.constructor(myTree.model);
// Rebuild the tree
myTree.postMixInProperties();
myTree._load();