how to set distance between cells in grid in cytoscape js? - cytoscape.js

I made a graph in cytoscape js using a grid layout and I specified the row and col for each node.
However the graph being rendered is very zoomed out and the spacing between the nodes is very large. I fixed this problem partially by specifying the number of rows and columns for the grid layout (see picture. Although specifying the number of rows and columns is not desired as the size of the graph can change.
The resulting graph (when specifying the rows and columns) is still not optimum, the nodes are still rendered too small and the distance between them horizontally is still quite large.
Is there anyway to specify the distance between the grid cells?
My current code for the grid layout looks as follows:
layout: {
name: 'grid',
padding: 30,
rows: 20,
columns: 10,
fit: true,
position: function( node ){ return {row:node.data('row'), col:node.data('col')}; }
}

The grid layout automatically uses the dimensions of your nodes and the available space to position the nodes in the graph. If you're looking for new functionality, please file an issue describing the feature you would like.
Thanks for using Cytoscape.js.

It maybe filthy hack, but it works for me.
Just find function in cytoscape code for grid layout:
GridLayout.prototype.run
In this function find lines like:
var cellWidth = bb.w / cols;
var cellHeight = bb.h / rows;
Just add some multipliers to cellWidth and cellHeight, like below:
var cellWidth = bb.w / cols * 2.5;
var cellHeight = bb.h / rows / 4;
Also you can extract multipliers like layout params.

Related

Cytoscape disappearing edges with bezier curves and nodes moving/longer labels in nodes

I am using cytoscape with bezier curves but edges are disappearing when having a node with longer label or just moving source node next to target node.
Video: https://www.screencast.com/t/N2f5eZ5M7
Runnable sample: https://stackblitz.com/edit/web-platform-vpl72r?file=index.html
I have already seen "Edge xxxxx has invalid endpoints and so it is impossible to draw" warning and searched corresponding threads but couldn't find a solution.
Does anyone know how to handle that ?
I see some warnings on the console
The style value of label is deprecated for width
The style value of label is deprecated for height
After I deleted 'width': 'label', 'height':'label', I no longer observe such problem.
--- Update 1.1 ---
but I'd like to have node's dimensions based on label's dimensions.
To do this I think you should write a function style. See the below example. In the example, you can see that we are setting the size of the nodes dynamically based on their name length. I assumed name is a string that stores labels.
cy.style().selector('node').style({
'width': (x) => { return x.data('name').length + 'px;' }
'height': (x) => { return x.data('name').length + 'px;' }
}).update();

constrain proportions while resizing images

I implemented drag and drop of images and now i want to constrain proportions of images while resizing.
/**
* Variable: constrainChildrenOnResize
*
* Specifies if children should be constrained according to the <constrainChildren>
* switch if cells are resized (including via <foldCells>). Default is false for
* backwards compatiblity.
*/
mxGraph.prototype.constrainChildrenOnResize = false;
i set this to true but its not working :s
What API/property i need for this functionality..
constrainChildrenOnResize is responsible for positioning and size of the children of resized cell. It means that children should keep their position relatively to the parent-cell.
In your case I would suggest to extend mxVertexHandler using union method. In this example you can see how to implement min-width/min-height restrictions. Using this example you are able to write your own rules for constrain.
Here is my simple solution:
var vertexHandlerUnion = mxVertexHandler.prototype.union;
mxVertexHandler.prototype.union = function (bounds) {
var result = vertexHandlerUnion.apply(this, arguments);
var coff = bounds.width / bounds.height
result.width = result.height * coff;
return result;
};
So this function is called every time you move mouse during dragging the resizer.
bounds - object, always same and represent old geometry of the cell (before resizing)
result - object, represents new values, which are going to be applied. Between this line ad return statement you can place any code you need to modify result.
In my simple example I just get the initial relation between width and height of the cell (coff) and then set new width by multiplying coff and new height. It will work if you drag corner or top/bottom. In real project this logic should be slightly extended, or you should make visible only corner handlers.
By the way, this code will work for all resizable cells on your graph. If you want to apply it only to images or some other kind of cells - you can put condition and check the cell type before recalculating. You can get current cell or its state via this.state.cell or this.state inside of union function.
For example only for vertecies:
... ...
var result = vertexHandlerUnion.apply(this, arguments);
if (this.state.cell.isVertex()) {
//calculations here
}
return result;

Sizing Node Relative to Label in cytoscape

So I'm aware you can use a function to compute a css property in Cytoscape.js e.g.
cytoscape.stylesheet()
.selector('node')
.css({
'width': function(ele) {
return 12;
})
I'm also aware of the special value 'label' that can be the value of the property e.g.
cytoscape.stylesheet()
.selector('node')
.css({
'width': 'label'
})
What I'm wondering is is there any particular property I could use to scale the label by some factor, e.g. what I want is something like
cytoscape.stylesheet()
.selector('node')
.css({
'width': function(ele) {
return labelWidth * 1.5; //Where to get labelWidth from
})
Specially I want the ability to be able to calculate the height and width of an ellipse so that the label is completely contained within the ellipse (which can be computed using some math e.g. Ellipse bounding a rectangle).
But I haven't been able to find a way to get the labelWidth. I did manage to do it using rscratch but only if the node actually got rendered twice (e.g. multiple .selectors), any proper way to get the label width and height from a given element (or at least a way to calculate how it'll be rendered?).
If you want to do more sophisticated sizing, your calculations are going to have to be more sophisticated.
Options :
(1) Calculate the dimensions of the text yourself using a div.
(2) Use the auto sizing, and then adjust the size based on the current size.
(1) is cleaner than (2).
It doesn't make sense for Cytoscape.js to expose rendered calculated values for you in the stylesheet. Those values are calculated from the stylesheet, creating a dependency cycle.
If you just want the label inside your node, you could just set the padding attribute to make more space around the text.

Circle layout with custom center coordinates

I'm building a dynamic graph with cytoscape.js over a world map generated with jVectorMap.
Starting with coordinates in LAT / LON format from json data, I can convert them in point.x / point.y over the generated map with:
var nodePoint = theMap.latLngToPoint(node.data.lat, node.data.lon)
and then I can add the new node in the graph with:
cy.add({
group: "nodes",
position: { x: nodePoint.x, y: nodePoint.y },
style: {
'background-color': node.color
},
data: {
id: node.id,
name: node.label,
customData: node.data,
}
})
This part works, but when json returns multiple nodes with the same coordinates, I would like to spread these nodes around the point with the common LAT / LON information.
I'm able to identify these nodes and apply a dedicated layout to them, but the resulting layount is located in the center of the screen / div of graph.
What I want to know is if there is a simple way to apply a Circle Layout with specific center point.
(I would like to avoid implementing the solution that I'm evaluating... i.e to build a bounding box around the center point and see what happens, (with the bounding box size that grows along with node number in the same point))
In the Wine and cheese example included in the documentation they use the bounding box to force a concentric layout around a selected node ( http://js.cytoscape.org/demos/cde4db55e581d10405f5/ ).
While you indicate you would like to avoid this solution it seems a straightforward solution. The implementation in the example is rather elegant.

Fix series order

I am producing a graph using the following code:
var svg = dimple.newSvg("#p_m", 1200, 200+(data.f_c_c*20));
var chrt_participants = new dimple.chart(svg, data.result);
chrt_participants.setBounds(200, 50, 900, 100+(data.f_c_c*20));
var y = chrt_participants.addCategoryAxis("y", ["title", "name"]);
var x = chrt_participants.addLogAxis("x", "Activity");
var s = chrt_participants.addSeries(["cid_id","cid","action"], dimple.plot.bar);
chrt_participants.addLegend(800, 0, 400, 40, "left");
chrt_participants.draw();
It draws the following graph:
Everything is as expected, except that the series (the coloured chunks of the bars) don't seem to be put in any particular order. As you can see, for two bars, for some reason unbeknownst to me, the red value is placed before the blue.
Is there a way to fix the order of the series?
The default order is descending by value but you can easily override using series.addOrderRule(). The documentation explains how:
https://github.com/PMSI-AlignAlytics/dimple/wiki/dimple.series#addOrderRule
On a side issue, be careful using a log axis for a stacked bar, it's very misleading. It's not immediately apparent that red and blue have about a 50/50 split of bars. You might be better using a grouped bar instead so that both categories receive comparable scaling.