How to center the layout of a particular collection about a point - cytoscape.js

I have a graph with a cose-bilkent layout. I'm trying to enable the user to search for a particular collection of nodes, and then have the program run a concentric layout for only those in the collection in order to zoom/concentrate on the collection of interest. All works well, except that in the process of making these nodes concentric, they are all moved to the bottom right corner of the screen (at least relative to the other nodes in the graph).
Is there an option for any of the built-in layouts to center the nodes being 'layed out' about a particular location in the viewport?

The option to "pin" a layout its through the use of the boundingBox layout option. Most (or all) layouts have this option.
You can try this:
let p = node.data(/* Your node position field */);
let l = nodes.layout({
name: 'concentric',
fit: false,
//your animation and padding options
boundingBox: {
x1: p.x - 1,
x2: p.x + 1,
y1: p.y - 1,
y2: p.y + 1
},
avoidOverlap: true,
concentric: function( ele ){
if( ele.same( node ) ){
return 2;
} else {
return 1;
}
},
levelWidth: function(){ return 1; },
});
l.run();
Adapted from this example, that share some similarity with your problem. It places everything from the neighborhood of a node you select in a concentric layout, without moving the selected node, only the other nodes.
You can find its complete source code here.

Related

Focus/Zoom in on part of the graph or a specific set of nodes or a single node

I am currently using Cytoscape to create a large graph with 500 nodes. I am using Dagre for the hierarchical layout.
Currently the graph is small on the screen because of number of nodes which makes sense but
Is there a way to focus on a single node once the graph is created. Or can I zoom in on a part of graph?
I have tried using animate filter on dagre but it animates the whole graph and does not focus or zoom in on one node or one part of the graph.
this.cy
.layout({
name: "dagre",
padding: 30,
animate: true, // whether to transition the node positions
animateFilter: function( node, i ){
if ( node[0]._private.data.type === "student") {
return true;
}
},
transform: function( node, pos ){ return pos; },
})
.run();
Also tried zoom, But I only want to zoom one or set of nodes at the same time
cy.zoom({
level: 1.1, // the zoom level
//How can I filter the nodes I want?
});

How do I make cose graph not snap into place?

I am creating a network graph using Cytoscape JS with the cose layout. The issue I am running into is that when i am animating my graph, it eases into its end position and then it snaps into a different position.
I have tried turning of the fit, defining boundingBox, increasing and decreasing the minTemp as well as increasing the coolingFactor.
The following is what layout variables are:
export default {
name: 'cose',
// Called on `layoutready`
ready: function(){},
// Called on `layoutstop`
stop: function(){},
// Whether to animate while running the layout
// true : Animate continuously as the layout is running
// false : Just show the end result
// 'end' : Animate with the end result, from the initial positions to the end positions
animate: 'end',
// Easing of the animation for animate:'end'
// Options: 'ease', 'linear'(default if undefined), ... potentially more options but need to find them
animationEasing: 'ease',
// The duration of the animation for animate:'end'
animationDuration: 3000,
// A function that determines whether the node should be animated
// All nodes animated by default on animate enabled
// Non-animated nodes are positioned immediately when the layout starts
animateFilter: function ( node, i ){ return true; },
// The layout animates only after this many milliseconds for animate:true
// (prevents flashing on fast runs)
animationThreshold: 1000,
// Number of iterations between consecutive screen positions update
refresh: 10,
// Whether to fit the network view after when done
fit: true,
// Padding on fit
padding: 20,
// Constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
boundingBox: undefined,
// Excludes the label when calculating node bounding boxes for the layout algorithm
nodeDimensionsIncludeLabels: true,
// Randomize the initial positions of the nodes (true) or use existing positions (false)
randomize: true,
// Extra spacing between components in non-compound graphs
componentSpacing: 10,
// Node repulsion (non overlapping) multiplier
nodeRepulsion: function( node ){ return 1000000000; },
// Node repulsion (overlapping) multiplier
// NOTE: effects spacing of nodes
nodeOverlap: 100000000,
// Ideal edge (non nested) length
idealEdgeLength: function( edge ){ return 50; },
// Divisor to compute edge forces
edgeElasticity: function( edge ){ return 1; },
// Nesting factor (multiplier) to compute ideal edge length for nested edges
nestingFactor: 0.1,
// Gravity force (constant)
gravity: -1000,
// Maximum number of iterations to perform
numIter: 20000,
// Initial temperature (maximum node displacement)
initialTemp: 100,
// Cooling factor (how the temperature is reduced between consecutive iterations
coolingFactor: 0.999,
// Lower temperature threshold (below this point the layout will end)
minTemp: 10.0,
};
If you want a more sophisticated force-directed layout, you should use COSE Bilkent or fCOSE. The bundled COSE is fast and small in file size but it has less features.

Can Cytoscape.js with Dagre layout draw a vertical tree?

Is it possible to configure this to display a vertical tree instead of a horizontal tree? What I mean by vertical is something similar to Windows Explorer.
We have some users that would prefer to work with it that way.
The dagre layout extension has a github page with some useful default values for the layout:
var defaults = {
nodeSep: undefined, // the separation between adjacent nodes in the same rank
edgeSep: undefined, // the separation between adjacent edges in the same rank
rankSep: undefined, // the separation between adjacent nodes in the same rank
rankDir: undefined // 'TB' for top to bottom flow, 'LR' for left to right,
ranker: undefined, // Type of algorithm to assign a rank to each node in the input graph. Possible values: 'network-simplex', 'tight-tree' or 'longest-path'
minLen: function( edge ){ return 1; }, // number of ranks to keep between the source and target of the edge
edgeWeight: function( edge ){ return 1; }, // higher weight edges are generally made shorter and straighter than lower weight edges
// general layout options
fit: true, // whether to fit to viewport
padding: 30, // fit padding
spacingFactor: undefined, // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up
nodeDimensionsIncludeLabels: false, // whether labels should be included in determining the space used by a node
animate: false, // whether to transition the node positions
animateFilter: function( node, i ){ return true; }, // whether to animate specific nodes when animation is on; non-animated nodes immediately go to their final positions
animationDuration: 500, // duration of animation in ms if enabled
animationEasing: undefined, // easing of animation if enabled
boundingBox: undefined, // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
transform: function( node, pos ){ return pos; }, // a function that applies a transform to the final node position
ready: function(){}, // on layoutready
stop: function(){} // on layoutstop
}
The important part here is the rankDir, you can define LR there :)

prevent node overlap on add new node

how do i prevent from overlapping new added nodes in cytoscape js graph?
i do not want change position of existing nodes
i want new adding nodes do automatically position by this rule that preventing existing node overlap
when i use add API to add nodes to graph added nodes overlap others
how can i change this behavior?
is there a solution for prevent node overlap in adding mode (new node do not overlap existing node)?
cy.add([
{ data: {label:"aaa" ,id:"bbb" } },
{ data: {label:"aaa333" ,id:"rrrr"} },
]);
There is no built in solution. I just look for an empty spot using this code for testing overlap (this is given a fixed node size, you could adapt it for using bounding boxes, but that slows things down).
function overlap(pos, size) {
var overlap = false;
cy.nodes(':visible').forEach(function (node) {
var npos = node.position();
if ((pos.x - size) < npos.x && (pos.x + size) > npos.x && (pos.y - size) < npos.y && (pos.y + size) > npos.y) {
overlap = true;
return false; //break
}
});
return overlap;
}
I use this with the prefered position as first argument ({x:..,y:..}), and when that fails I just add 10 to y to see if it fitst. Wether this is the best solution for you depends on the specific problem.
If you want an automated way of doing this, your best bet is a force-directed/physics layout.
Lock all but the new nodes when you run the layout.
You'll have to experiment with different layouts to see which particular one suits your app/data best.

Using the edge weight for force directed layout (CoSE) in cytoscape.js

I am not sure how best to utilize the edge weight (e.g. strength of interaction between two interacting proteins) while generating a force directed layout using the CoSE plugin in cytoscape.js. Could someone provide any pointers. Should it be "idealEdgeLength" or "edgeElasticity"?
(EDIT) Following is a figure showing what I currently get (A) and what I am trying to achieve (B). Also below are the parameters I used for generating the layout.
Thanks,
Datta.
PS: Click to view a figure showing the current (labelled "A") and expected (labelled "B") layouts. Following are layout options for "A".
var options = {
name: 'cose',
// Called on `layoutready`
ready: function () { },
// Called on `layoutstop`
stop: function () { },
// Whether to animate while running the layout
animate: true,
// Number of iterations between consecutive screen positions update (0 -> only updated on the end)
refresh: 20,
// Whether to fit the network view after when done
fit: true,
// Padding on fit
padding: 30,
// Constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
boundingBox: undefined,
componentSpacing: 100,
// Whether to randomize node positions on the beginning
randomize: true,
// Whether to use the JS console to print debug messages
debug: false,
// Node repulsion (non overlapping) multiplier
nodeRepulsion: 400000,
// Node repulsion (overlapping) multiplier
nodeOverlap: 10,
// Ideal edge (non nested) length
idealEdgeLength: 10,
// Divisor to compute edge forces
edgeElasticity: 100,
// Nesting factor (multiplier) to compute ideal edge length for nested edges
nestingFactor: 5,
// Gravity force (constant)
gravity: 80,
// Maximum number of iterations to perform
numIter: 10000,
// Initial temperature (maximum node displacement)
initialTemp: 100,
// Cooling factor (how the temperature is reduced between consecutive iterations
coolingFactor: 0.95,
// Lower temperature threshold (below this point the layout will end)
minTemp: 1.0
};
You can specify functions instead of static numbers for some key CoSE layout settings. The functions take edges (or nodes, in some cases), so you can tailor the layout based on edge properties.
So you could do something like this:
idealEdgeLength: function (edge) {
// Default is: 10
// Instead, base it on "weight"
return edge.data().weight * .5
},
edgeElasticity: function (edge) {
// Default is: 100
// Instead, base it on "weight"
return edge.data().weight * 4
},
You'll have to experiment with ranges that work with the layout engine and the range of weight you are expecting as input, but that approach should work AOK.
The ideal edge length specifies a default-like value, whereas the elasticity specifies one of the forces that determine the final length. In general for physics simulation layouts, you will need to experiment with different values and evaluate the results for your specific graphs.