Dynamically combining 'preset' layouts - cytoscape.js

I'm trying to dynamically place different metabolic pathways (nodes following a preset layout contained in a parent node).
Normally i would define positions for each child node, so as to not overlap the parent nodes. However the graphs( = generated json containing the requested nodes) are generated dynamically, so this is not an option.
Is there a way to achieve this, short of recalculating positions for each node as the json is being generated.
E.g.: requesting glycolysis -> TCA -> Urea
Current situation
Acceptable solution

If you want to do something manual like that you'll probably have to use some code rather than just specify a layout.
let shiftPosition = (pos, delta) => ({ x: pos.x + delta.x, y: pos.y + delta.y });
let shiftNode = (node, delta) => shiftPosition( node.position(), delta );
let findDelta = parent => ({ x: 100, y: 100 }); // determine yourself
cy.nodes(':parent').forEach( parent => {
let delta = findDelta( parent );
parent.children().positions( node => shiftNode( node, delta ) );
} );

Related

How to remove specific edges without redrawing the graph using cytoscape.js?

Given a node in cytoscape.js, I'm trying to remove the node and all directed (in and out) edges of that node. I've wrote some quick and dirty filters to do so, but calling cy.remove(edges); prints a 'no such method exists' error message to the console. Removing a node through the same approach works fine, but removes all nodes connected to the removed node, which in my case, is the whole graph (most of the times). So I've tried removing the edges and the node from my data source and then re-drawing the graph, but this is not the correct approach since it re-draws the graph, and changes the layout( i.e. refreshes the canvas).
How can I solve this?
//data is the id of the node I want to remove
var filteredEdges = this.elements.edges.filter((x, idx, arr) =>
{
return x.source != data && x.target != data ;
});
var removedEdges = this.elements.edges.filter((x, idx, arr) =>
{
return x.source == data || x.target == data ;
});
//this.cy.remove(removedEdges); this fails
this.elements.edges = filteredEdges;
var filteredNodes = this.elements.nodes.filter((x, idx, arr) =>
{
return x.id != data;
});
this.cy.remove(data);
this.elements.nodes = filteredNodes;
this.render(); // this re-draws the whole graph, not a useable approach
Solved it using selectors. The documentation for cytoscape.js is very lacking. For anyone encountering this issue sometime in the future, here's how I did it:
this.cy.remove('edge[source=\'' + nodeId + '\']');
this.cy.remove('edge[target=\'' + nodeId + '\']');
this.cy.remove('#' + nodeId);

Angular 2: How to attach unique div id in multiple tabs

I have several tabs with same page elements. One of the element contains a map div based on location details. I could get the map initialized for the first tab opened. The next tab when I open it throws up error as the div id containing the map is already initialized. How to make the div id unique (dynamic) for each map initialization in different tabs? Any idea would help me!
You can use this function to generate a completely unique id which you can then set with [id]="getUniqueId()":
getUniqueID(): string {
let d = Date.now();
if (window.performance && typeof window.performance.now === 'function') {
d += performance.now();
}
const id = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
const r = (d + Math.random() * 16) % 16 | 0;
d = Math.floor(d/16);
return (c === 'x' ? r : (r&0x3|0x8)).toString(16);
});
return id;
}

dojo how to get portlet position object that have value(column,row,width,height) in GridContainer?

hi my server tries to store information about portlet. I would like to get information(column,row,width,height) about the location of the portlet. but I think I have. more difficult to me..
for example there are two single portlet in GridContainer. I want to get information about the portlet on the column,row and the value of the width, height.if i imported value one of two portlet in GridContainer, the value of the first portlet row: 0, column: 0, width: 50px, height: 50px, the value of the second portlet row: 0, column: 1, width: 50px , height: 50px.
ask for advice.
my code
var selectedForm=registry.byId('tabContainer').get('selectedChildWidget');
var grid=selectedForm.getChildren();->have portlets in GridContainer
var oldcolumn=null;
var row=1;
for(var i =0; i<grid.length;i++)
{
for(var j=0;j<grid[i].length;j++){
if (oldcolumn==null)
oldcolumn=grid[i].getChildren()[j].domNode.parentNode.cellIndex;
console.log(' column:'+grid[i].getChildren()[j].domNode.parentNode.cellIndex);
if(oldcolumn == nodelist[i].domNode.parentNode.cellIndex)
console.log('column:'+grid[i].getChildren()[j].domNode.parentNode.cellIndex+'row:'+(++row));
else
{
row=1;
oldcolumn=grid[i].getChildren()[j].domNode.parentNode.cellIndex;
console.log(' column:'+grid[i].getChildren()[j].domNode.parentNode.cellIndex'row:'+(++row));
}
}
}
Column
If I read the documentation about the dojox/layout/GridContainer, it states the following:
By requiring the GridContainer, two new attributes are mixed into the
dijit/_WidgetBase class:
column: Holds the current column a widget is in. (If any). Defaults to “1”
dragRestriction: Is the widget draggable? You can use this to prohibit the dragging of a specific Widget
This means you could use the following to get the column:
myPortlet.get("column");
Row
There is no such thing as a "row", so I suppose you want to retrieve the zone. There's no way to retrieve the zone of the portlet though, but you can calculate it by yourself. If you use getChildren() on the GridContainer, the portlets are always returned in the same order based on their column and then their zone. However, this is undocumented and may change in the future.
So this means you could easily write a counter that increments each time a portlet is in the same column (which means he's in the next zone). For example:
var children = registry.byId("myGrid").getChildren();
var perColumn = [ ];
arrUtils.forEach(children, function(portlet) {
var id = portlet.get("id");
var col = portlet.get("column");
if (perColumn[col] !== undefined) {
perColumn[col]++;
} else {
perColumn[col] = 0;
}
var zn = perColumn[col];
console.log(id + ": " + col + " " + zn);
});
Get width + height of portlet
The width and the height of the portlet are determined by its content, so no width or height properties are available. However, you could get the DOM node of the content and retrieve the width and height from it using dojo.position (in recent versions it's the dojo/dom-geom module), for example:
require([ "dojo/dom-geom" ], function(domGeom) {
var box = domGeom.position(myPortlet.get("domNode"));
console.log(box.w + " " + box.h);
});
I also made an example to retrieve all properties (column + zone + width + height): http://jsfiddle.net/dF94F/

How to create custom style mapping in cytoscape.js?

Is there a way to add a custom mapper to the new cytoscape.js?
I know there is data(nodeKey), but that uses nodeKey's value de novo. Can I set a mapping of my own?
Thanks!
Custom mappers are too expensive in general, so they are not supported in Cytoscape.js. Good performance is one of our top requirements for the library.
If you would describe the sort of mapping you're looking for, it may be possible with the API today, or we could work something out that meets your needs. Thanks!
Here is my custom mapper:
/* Converts element attributes to their appropriate mapped values
* Any non-matching attributes will be matched to the "other" mapping
* if exists
* data: data
* elementType: nodes or edges
* attr: some key under data.nodes[i].data
* mapping: obj mapping oldVal: newVal for attr
* (toType): new values will be put into this attr, if attr
* shouldn't be touched
*/
function mapAttr(elementType, attr, mapping, toType){
for(var i=0; i < data[elementType].length; i++){
element = data[elementType][i]['data'][attr];
toType = toType ? toType : attr;
if( mapping[element] ){
data[elementType][i]['data'][toType] = mapping[element];
}else if(mapping['other']){
data[elementType][i]['data'][toType] = mapping['other'];
}
}
}
Example:
var nodeShapeMapper = {
Rearrangement: "hexagon",
Gene: "octagon",
Molecule: "triangle",
other: "ellipse"
};
mapAttr('nodes', 'ntype', nodeShapeMapper, 'shape');
This generates values for the "shape" node attribute according to nodeShapeMapper[ntype]

Selection of first node of a tree is not happening with xtype treecolumn

I have created a tree panel by specifying the xtype as treecolumn. I want to select the first leaf of the tree. In this example I have registered the boxready event detailed below:
boxready : function( treePanel, width, height, eOpts ){
treePanel.getSelectionModel().select( 0 );
//treePanel.select( treePanel.getRootNode().getChildAt(0) );
treePanel.getSelectionModel().selected = 0;
},
treePanel.getSelectionModel()
This example is giving me selectionmodel of type SINGLE. Can anyone explain why my example does not select the first leaf?
This is a little "diagram":
If you need the leaf from beginning with:
one node selected:
var nodeData = treePanel.getSelectionModel().getSelection();
from the begin:
var node = treePanel.getRootNode(); -- father ( first Node );
findLeaf : function(node){
if(node.isLeaf()){
// this is the node that u want
}else{
// bucle to find it
node.eachChild(function(nodeChild,array){
if(nodeChild.isLeaf()){
// this is the node that u want
}else{
// get childs of this node
if(nodeChild.hasChildNodes()){
//find the childs from this node.
this.findLeaf(nodeChild);
}
}
});
}
};