Add compound node on demand cytoscape.js - cytoscape.js

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.

Related

Cytoscape layouts - Handle locked nodes

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).

trying to expand out nodes to null parent

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.

Cytoscape.js selecting collapsed nodes

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.

Sencha Touch 2: Insert into TreeStore/NestedList

I'm using a NestedList with a underlying TreeStore. Now I want to add items to the NestedList as leafs.
How can I do this?
Currently my code (Controller, onAddButtonTapped) looks like this:
var store = Ext.getStore('menuStore');
var customerAreaNode = store.getRoot().getChildAt(1);
customerAreaNode.appendChild({name: "text", leaf:true});
customerAreaNode.expand();
store.sync();
This code results in two new empty listentries on leaf level (behind the correct node) and one new listentry on node level.
Every new entry has no names shown in the NestedList but every item contains "text" in their name field. Curiously one of the new entries at leaf level is not typed to the underlying Model. So the model-corresponding methods could't be found:
Uncaught TypeError: Cannot call method 'getSelectedName' of undefined
Does anybody know a easy tutorial how to add data into NestedList/TreeStore? I could not find one good example in the sencha touch docs.
The default display field for leaf items is "text". You can get the information from here. If you want to use "text" as display field, then you need to change this line to
customerAreaNode.appendChild({text: "text", leaf:true});
Or you can change your nested list's display field, so your model do not need to change for this time.
yournestedlist.setDisplayField('name');
Hope this helps.
In my case i used to update root and child nodes like the following way
Ext.getStore('Contactsstore').getAt(1).getChildAt(0).set('Email',contactemail);
Ext.getStore('Contactsstore').getAt(1).set('ContactTitle',contacttitle);

libxml - looping through all children of a child

I am new to libxml. I would like to write 1 loop to loop through all the children of a child node etc. e.g.
<par>i want to <bold>loop <italic>through </italic> all</bold> children in this node</par>
At the moment my looping code look as follows but I only get the "bold" node and not the "italic" child.
if (xmlStrEqual(node->name, BAD_CAST "p")) {
xmlNodePtr child = node->children;
while (child != NULL) {
child = child->next;
}
}
It is conceivable that the node structure could grow to 4-5 elements, so I need a solution which is more robust that putting while loops within while loops. Any help would be appreciated please.
The xmlTree interface you're using is heirarchical. <par> has 3 children - the text before <bold>, <bold> itself, and the text after. <bold> has children of its own, which includes <italic>. The node->children list only contains the immediate children of a node.
There are a couple ways to get the behavior that you want.
While processing child, you can recursively process child->children to get to <bold>'s child, which includes <italics>
If you're looking SPECIFICALLY for that <itallic> (or for a set of specific nodes), you can use an XPath expression, such as "par//italic" to find any italic node anywhere beneath par.
http://www.xmlsoft.org/examples/index.html#XPath
You could look at libxml2's xmlTextReader interface rather than this xmlTree. It provides the serial "read through" of the document that you're expecting.
http://xmlsoft.org/xmlreader.html
Each method has its own advantages/disadvantages, depending on what all you're trying to do with your application.