The cytoscape.js-cola documentation for the gapInequalities element of a layout dictionary says:
gapInequalities: undefined, // list of inequality constraints for the gap between the nodes,
// e.g. [{"axis":"y", "left":node1, "right":node2, "gap":25}]
How do you set up the objects that specify the nodes in the values of left and right?
maxkfranz helpfully pointed out here that these objects need to be collection objects. These contain references to specified nodes ("elements") and are created by querying the cytoscape.js "core object". What's not clear to me is, given that the layout object needs to refer to the node elements, and the elements should not be added to the graph before the layout tells how to render them, how do you properly set up those collection objects?
For example, to specify that node b should be placed above node a, what goes in place of ???a??? and ???b??? in the code below?
cy = cytoscape({
elements: [
{ data: { id: 'a' } },
{ data: { id: 'b' } },
. . .
],
layout: {
name: 'cola',
gapInequalities: [
{ axis: 'y', left: ???'a'???, right: ???'b'???, gap: 25 }
. . .
],
. . .
}
. . .
]);
In this case, the answer can't be cy.$id('a') and cy.$id('b') because the cy object hasn't been created yet. You could get around that by creating the cy object without elements and then calling cy.add() to put them in. But then what goes in the layout object that's passed to cytoscape()?
I'm new to both cytoscape.js and cola.js, so I'm most likely just missing some very elementary idea here. A simple example showing what function calls set up the objects and the sequence in which to call them would probably do it. In my application, nodes and edges are added to the graph gradually, and the animation needs to show them being added, so not having all the elements set up at the start makes more sense, anyway.
Since your nodes and edges are added gradually, you can create cy object without elements and layout object. Then when new nodes come, you can add them to the graph and apply layout.
After initialization of the cy object, every time new nodes come, apply the following :
cy.add(...);
cy.layout({
name: 'cola',
gapInequalities: [{ axis: 'y', left: cy.$id("a"), right: cy.$id("b"), gap: 25 }],
...
}).run();
Related
I'm writing an application in Vue that will have a map in it. I want to add some points in that map. The code relevant to the map is,
<div>
<vl-map :load-tiles-while-animating="true" :load-tiles-while-interacting="true" data-projection="EPSG:4326" style="height: 400px">
<vl-view :zoom.sync="zoom" :center.sync="center" :rotation.sync="rotation"></vl-view>
<vl-layer-tile>
<vl-source-osm></vl-source-osm>
</vl-layer-tile>
<vl-layer-vector>
<vl-source-vector :features="points"></vl-source-vector>
</vl-layer-vector>
</vl-map>
</div>
Here points is part of data points: []
If I write the points directly in data everything works.
Like,
points: [
{
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [0.0, 0.0]
},
properties: {}
]
shows a point in [0, 0].
However, I need to use a method that will call several APIs to find out the points that I need to show. It's when I write the code to prepare the points to have the correct value when everything stops working. I'm writing this code in the mounted section.
async calculatePoints () {
this.points = // points as I want them to be
}
But this is not working (although some few times works). Any idea what is going on here or which is the best way to handle this?
I have to provide a copy/paste functionality using the dagre layout. The idea is the user copies a node and where ever they decide to "paste" it, the hierarchy of nodes copied, will be created there. This would mean that all nodes in the way would have to move.
I first thought maybe I could call layout again but that doesn't "fit" them in.
I'm still learning cytoscape.js so if this is a simple question, please excuse me.
This would be something quite hard to achieve, so hear me out:
First step:
Cytoscape has a extension called context-menus (demo)
Another extension is called clipboard (demo)
make yourself familiar with these two
Second step:
create your graph with these two extensions and a dagre graph
when you copy these nodes, make the copy function behave like this:
add the nodes with their edges in the right hierarchy
when you right click on a node, add a insert into hierarchy function, which adds the copied nodes into the graph
Code examples:
var options1 = {
// List of initial menu items
menuItems: [
{
id: 'addToHierarchy',
content: 'Add to hierarchy',
tooltipText: 'Add nodes to hierarchy here',
selector: 'node',
onClickFunction: function () {
// your handlerFunction
},
disabled: false
}
]
};
var instance = cy.contextMenus( options1 );
var options2 = {
clipboardSize: 0,
// The following 4 options allow the user to provide custom behavior to
// the extension. They can be used to maintain consistency of some data
// when elements are duplicated.
// These 4 options are set to null by default. The function prototypes
// are provided below for explanation purpose only.
// Function executed on the collection of elements being copied, before
// they are serialized in the clipboard
beforeCopy: function(eles) {},
// Function executed on the clipboard just after the elements are copied.
// clipboard is of the form: {nodes: json, edges: json}
afterCopy: function(clipboard) {},
// Function executed on the clipboard right before elements are pasted,
// when they are still in the clipboard.
beforePaste: function(clipboard) {},
// Function executed on the collection of pasted elements, after they
// are pasted.
afterPaste: function(eles) {}
};
var clipboard = cy.clipboard(options2);
I'm using Cytoscape.js to represent a network diagram. I can highlight more than one node at a time by selecting the first node followed by the second node while holding the shift key.
How can I have all the node ids that I have selected available in Javascript? For example and this would be ideal, if all the node ids that have simultaneously selected where organized into a Javascript list.
I looked in the cytoscape.js docs and could not find the location where this is described.
To receive ids of all selected:
elements
var typeIds = cy.elements(':selected');
nodes
var typeIds = cy.elements('node:selected');
edges
var ConnIds = cy.elements('edge:selected');
In order to get Ids of all selected nodes you could try this:
cy = window.cy = cytoscape ({
container : document.getElementBy('cy'),
// define a style for selected node
style : cytoscape.stylesheet()
.selector('node')
.css ({..............})
.selector('edge')
.css({...............})
.selector('node:selected')
.css({ 'border-color' : 'red'})
}),
elements : eles,
layout : {.............}
});
Is it possible to have multiple cytoscape graph instances that are unrelated but share the same source elements?
Here is an example of what i'm trying to do : https://jsfiddle.net/fa8hbdnh/
var elements = [
{ data: { id: 'n1'}, position: {x:100, y: 100}},
{ data: { id: 'n2'}, position: {x:150, y :150}},
//--->Edges--->
{ data: {id: 'e1', source: 'n1', target: 'n2'}},
];
var graph1 = cytoscape({
headless: true,
elements: elements
});
var graph2 = cytoscape({
headless: true,
elements: elements
});
graph1.elements()[0].data('foo',100); // Only changing graph1...
console.log(graph2.elements()[0].data('foo')); // ...however graph2 is also modified
(this requires the cytoscape library - http://js.cytoscape.org/)
As you can see, I change graph1 but graph 2 is also affected. Is there a way to save data elements on one instance without affecting other instances?
Cytoscape.js just takes in what you pass it. It doesn't make any assumptions about your JSON, and it doesn't copy data -- because that would slow down >=90% of usecases.
Either copy your JSON before you pass it in or pass collections from the first instance to subsequent instances. Collections are always copied from one instance to another, as noted in the docs, because that's the only way passing collections makes sense.
I want to display several instances of cytoscape in a single page, in a time sequence: first one set of nodes are displayed on the graph, the user must interact with it (create edges), then he moves to a second graph (#cy0 is :hidden and #cy1 is :visible).
For code optimisation sake I wish to use the same initialisation function to display different successive sets of nodes. My initialisation function works fine in the first instance, but the graph is not created (cy.initrender() == false) in the second session. A command is probably missing, I tested a couple, but I don't see what to do.
Here is my code:
//elements
$(function(){ // on dom ready
var elesJson = {
nodes: [
{ data: { id: 'S', faveShape: 'rectangle',} }
...
],
edges: [
{ data: { id: 'loan', source: 'B', target: 'U' } },
...
],
};
// instance index
var indexLevel=0;
// cy initialisation
$("#cy"+indexLevel).cytoscape({
style: cytoscape.stylesheet()...
elements: elesJson,
ready: function(){
window.cy = this;});
// jQuery command to move from one instance to the other.
$('#next').click(function(){
$("#cy"+indexLevel).css("visibility","hidden");
indexLevel++;
$("#cy"+indexLevel).css("visibility","visible");
cy.load(elesJson);
cy.ready();
console.log(cy.initrender());
});
I am able to generate my node.collection, it is not empty, but the canvas element is not created and/or displayed within the #cy div, and cy.initrender() returns "false".
Any solution to this?
As noted in the docs for init, you must call cy.resize() if you play around with the cy div's display or position: http://js.cytoscape.org/#core/initialisation
cy.resize() : http://js.cytoscape.org/#core/viewport-manipulation/cy.resize
Edit: You may want to use z-index instead to simplify things...