Y-axis force with cola.js and Cytoscape - javascript

I have noticed that using Cola.js (with Cytoscape.js) most of my layouts tend to form in a square layout and not using up my bounding box which is more wide than tall.
I've been looking around and found d3-force which has this option of forceY that transform a square layout (https://bl.ocks.org/steveharoz/8c3e2524079a8c440df60c1ab72b5d03):
To this more wide layout:
I would really like to do the same for Cola.js, however I have been struggling to do it and tried all possible options like setting the bounding box, disabling zoom and etc. Is this possible at all?
I've found a demo for Cola.js that provides somewhat what I need, but unable to make it work in Cytoscape.js: https://ialab.it.monash.edu/webcola/examples/pageBoundsConstraints.html

Based on the link you provide, you can apply a similar functionality with cola.js. You need to have two dummy nodes locked (one for top-left and one for bottom-right) and then add constraints for all other nodes appropriately. You can disable the visibility of dummy nodes (I left them as visible so we can see the top-left and bottom-right of the bounding box.). You can play with the position of dummy nodes to adjust your bounding box.
var cy = window.cy = cytoscape({
container: document.getElementById('cy'),
style: [{
selector: 'node',
css: {
'content': 'data(id)',
'text-valign': 'center',
'text-halign': 'center'
}
},
{
selector: 'edge',
css: {
'curve-style': 'straight',
}
}
],
layout: {
name: 'preset'
},
elements: {
nodes: [{
data: {
id: 'n0'
}
},
{
data: {
id: 'n1'
}
},
{
data: {
id: 'n2'
}
},
{
data: {
id: 'n3'
}
},
{
data: {
id: 'n4'
}
},
{
data: {
id: 'd0'
},
position: {x: 0, y:0}
},
{
data: {
id: 'd1'
},
position: {x: 400, y:150}
}
],
edges: [{
data: {
id: 'n0n1',
source: 'n0',
target: 'n1'
}
},
{
data: {
id: 'n1n2',
source: 'n1',
target: 'n2'
}
},
{
data: {
id: 'n2n3',
source: 'n2',
target: 'n3'
}
},
{
data: {
id: 'n4n1',
source: 'n4',
target: 'n1'
}
}
]
}
});
var tl = cy.getElementById('d0');
var br = cy.getElementById('d1');
tl.lock();
br.lock();
var realGraphNodes = cy.nodes().difference(tl.union(br));
var constraints = [];
for (var i = 0; i < realGraphNodes.length; i++) {
constraints.push({ axis: 'x', left: tl, right: realGraphNodes[i], gap: 100 });
constraints.push({ axis: 'y', left: tl, right: realGraphNodes[i], gap: 100 });
constraints.push({ axis: 'x', left: realGraphNodes[i], right: br, gap: 100 });
constraints.push({ axis: 'y', left: realGraphNodes[i], right: br, gap: 100 });
}
cy.layout({name: 'cola', randomize: true, animate: false, gapInequalities: constraints}).run();
body {
font: 14px helvetica neue, helvetica, arial, sans-serif;
}
#cy {
height: 95%;
width: 95%;
left: 0;
top: 0;
position: absolute;
}
<html>
<head>
<meta charset=utf-8 />
<meta name="viewport" content="user-scalable=no, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, minimal-ui">
<script src="https://unpkg.com/cytoscape#3.10.0/dist/cytoscape.min.js"></script>
<script src="https://unpkg.com/webcola/WebCola/cola.min.js"></script>
<script src="https://unpkg.com/cytoscape-cola/cytoscape-cola.js"></script>
</head>
<body>
<div id="cy"></div>
</body>
</html>

Related

Cytoscape.js Show/Hide labels on elements in the graph on button click

I have a node application using cytoscape v3.15.2. I have the styling set as follows
let cy = cytoscape({
container: document.getElementById('graph-div'),
style: [
{
selector: 'node',
style: {
'label': 'data(id)',
}
},
{
selector: 'edge',
style: {
'label': (ele) => {
if(ele.data('type') === '1') return 'data(category1)';
if(ele.data('type') === '2') return 'data(category2)';
return value;
}
}]
});
Now, using a checkbox, I am trying to show/hide the labels on the elements. I have tried doing the following:
cy.elements().style("label","");
But this doesnt work. The same thing works when I pass a random string instead an empty string, something like this : cy.elements().style("label","random");. Doing this sets the labels of all elements in the graph to hidden. Could you please help me how to do this. Thanks
You can manage showing/hiding labels by specifying a class in the stylesheet and then toggling it on button click as in the below example.
var cy = window.cy = cytoscape({
container: document.getElementById('cy'),
layout: {name: 'grid', rows: 2},
style: [{
selector: '.hasLabel',
css: {
'label': (ele) => {
if(ele.isNode()) return ele.data('id');
if(ele.isEdge()) return ele.data('weight');
}
}
}
],
elements: {
nodes: [{
data: {
id: 'n0'
}
},
{
data: {
id: 'n1'
}
},
{
data: {
id: 'n2'
}
},
{
data: {
id: 'n3'
}
}
],
edges: [{
data: {
id: 'n0n1',
source: 'n0',
target: 'n1',
weight: 3
}
},
{
data: {
id: 'n1n2',
source: 'n1',
target: 'n2',
weight: 5
}
},
{
data: {
id: 'n2n3',
source: 'n2',
target: 'n3',
weight: 7
}
}
]
}
});
document.getElementById("labelButton").addEventListener("click", function() {
cy.elements().toggleClass('hasLabel');
});
body {
font: 14px helvetica neue, helvetica, arial, sans-serif;
}
#button {
z-index = 1000;
}
#cy {
height: 95%;
width: 95%;
left: 0;
top: 50;
z-index = 900;
position: absolute;
}
<html>
<head>
<meta charset=utf-8 />
<meta name="viewport" content="user-scalable=no, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, minimal-ui">
<script src="https://unpkg.com/cytoscape#3.10.0/dist/cytoscape.min.js">
</script>
</head>
<body>
<button id="labelButton" type="button">Show/Hide Labels</button>
<div id="cy"></div>
</body>
</html>

Can a edge in cytoscape have multiple colors

I am trying to create a edge with multiple colors, not sure how to go about it.
Ideally each half of the link would have their color updated independently.
Want to know if this is feasible.
Thanks a lot
:)
Looks like it is! Check https://js.cytoscape.org/#style/edge-line and https://js.cytoscape.org/#style/gradient.
The strategy would be to break the line-gradient into color-stop positions, each with an associated color like this:
var cy = (window.cy = cytoscape({
container: document.getElementById("cy"),
boxSelectionEnabled: false,
autounselectify: true,
style: [{
selector: "node",
css: {
"label": "data(id)",
"text-valign": "center",
"text-halign": "center",
"background-color": "data(faveColor)"
}
},
{
selector: "edge",
css: {
"line-fill": "radial-gradient",
"line-gradient-stop-colors": "red green blue",
"line-gradient-stop-positions": "25 50 75"
}
}
],
elements: {
nodes: [{
data: {
id: "a",
faveColor: "#2763c4"
}
},
{
data: {
id: "b",
faveColor: "#37a32d"
}
},
{
data: {
id: "c",
faveColor: "#37a32d"
}
}
],
edges: [{
data: {
source: "a",
target: "b"
}
},
{
data: {
source: "a",
target: "c"
}
}
]
},
layout: {
name: "dagre"
}
}));
cy.ready(function() {
cy.dblclick();
});
var nid = 0;
cy.bind('dblclick', function(evt) {
console.log('dblclick');
cy.add({
group: 'nodes',
data: {
id: nid,
faveColor: 'red'
},
position: {
x: evt.x,
y: evt.y
}
});
nid++;
});
cy.bind('click', 'node', function(evt) {
console.log('node clicked: ', evt.target.id());
});
body {
font: 14px helvetica neue, helvetica, arial, sans-serif;
}
#cy {
height: 100%;
width: 100%;
float: right;
position: absolute;
}
<html>
<head>
<meta charset=utf-8 />
<meta name="viewport" content="user-scalable=no, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, minimal-ui">
<script src="https://unpkg.com/cytoscape#3.3.0/dist/cytoscape.min.js">
</script>
<!-- cyposcape dagre -->
<script src="https://unpkg.com/dagre#0.7.4/dist/dagre.js"></script>
<script src="https://cdn.rawgit.com/cytoscape/cytoscape.js-dagre/1.5.0/cytoscape-dagre.js"></script>
<script src="https://unpkg.com/cytoscape-dblclick/dist/index.js"></script>
</head>
<body>
<div id="cy"></div>
</body>
</html>

How to hide compound nodes while still displaying children in cytoscape.js

I want to create a graph where a few nodes are "compound" nodes, embedding several children nodes, but without displaying them: the children nodes should be displayed but the compounds should be hidden.
As a layout I am using an extension for cytoscape.js called dagre-cytoscape.
So far I created a 'compound' class that only the compound nodes have, and I tried to set opacity: 0 or visibility: hidden for this class only, but which each of these options the children nodes became invisible as well.
Here is a bin of a non-working example:
<!DOCTYPE html>
<html>
<head>
<meta content="text/html;charset=utf-8" http-equiv="Content-Type">
<meta content="utf-8" http-equiv="encoding">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1, maximum-scale=1">
<title>dagre demo</title>
<script src="https://unpkg.com/cytoscape/dist/cytoscape.min.js"></script>
<script src="https://unpkg.com/dagre#0.7.4/dist/dagre.js"></script>
<script src="https://cytoscape.org/cytoscape.js-dagre/cytoscape-dagre.js"></script>
<style>
body {
font-family: helvetica;
font-size: 14px;
}
#cy {
width: 75%;
height: 100%;
position: absolute;
left: 200;
top: 200;
z-index: 999;
}
</style>
<script>
window.addEventListener('DOMContentLoaded', function () {
var cy = window.cy = cytoscape({
container: document.getElementById('cy'),
layout: {
name: 'dagre'
},
style: [{
selector: 'node',
style: {
'background-color': 'teal',
'label': 'data(id)',
'visibility': 'visible'
}
},
{
selector: '.compound',
style: {
'visibility': 'hidden'
}
}
],
elements: {
nodes: [{
data: {
id: 'n0'
}
},
{
data: {
id: 'n1',
parent: 'parent'
}
},
{
data: {
id: 'n2',
parent: 'parent'
}
},
{
data: {
id: 'parent'
},
classes: 'compound'
}
],
edges: [{
data: {
source: 'n0',
target: 'n1'
}
},
{
data: {
source: 'n0',
target: 'n2'
}
}
]
}
});
});
</script>
</head>
<body>
<h1>cytoscape-dagre test</h1>
<div id="cy"></div>
</body>
</html>
jsbin version
In this example the nodes n1 and n2 are hidden, as well as the parent node. I would like to keep parent hidden but have n1 and n2 visible.
Try setting background-opacity and border-width of compound nodes to 0.

C3 char inside flexbox

I want to have a c3 chart that fulfill a web page. I am trying to use Flexbox.
I use Flex-direction column and add a header and a div. The div as the only grow element. It seems to work well while I resize to upper sizes but its broken when I shrink the windows
See example at: https://codepen.io/josubg/full/RQNJyN/
function showChart(config){
return c3.generate(config);
}
function fixSize() {
var a =document.getElementById("widget-container");
cs.resize({"height": a.clientHeight, "width":a.clientWidth})
}
var labels = ['Month 0', 'Month 18', 'Month 24', 'Month 30', 'Month 36'];
var values = ['Index', 4, 4, null, null, null];
// Include a mark for denote that are axis labels
labels.unshift('x');
var config = {
bindto: d3.select("#chart_a"),
onresized: fixSize,
line: {
connectNull: true
},
legend: {
show: false
},
data: {
x : "x",
columns: [
labels,
values
],
type: "bar"
},
grid: {
show: true,
y: {
lines: [
{value: 8, text: 'Goal', class: 'goal'},
{value: 4, text: 'Baseline', class:'baseline'}
]
}
},
axis: {
x: {
type: 'category'
},
y:{
show:false,
max: 9
}
}
};
cs = showChart(config);
cs.flush();
body{
height: 100vh ;
width: 100vw ;
display: flex;
flex-direction: column;
overflow: hidden;
}
#widget-container{
background-color: fuchsia;
flex-grow: 1;
}
div.c3{
height: 100% !important;
width: 100% !important;
max-height: 100% !important;
}
<html lang="en">
<head>
<link href="https://cdnjs.cloudflare.com/ajax/libs/c3/0.4.18/c3.min.css" rel="stylesheet">
</head>
<body>
<h1>A chart!</h1>
<div id="widget-container">
<div id="chart_a"></div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/c3/0.4.18/c3.min.js"></script>
</body>
The awful backgrounds are to ease view the elements size.

Expanding nodes with Cytoscape

I am trying to make a graph that, when the page loads, expands all the nodes in the graph out from the root node like a tree, like the opposite of this example. The graph is directed with no cycles and a pre-defined root node.
Right now, I hide all the nodes on the graph. I then run a DFS from the root node. At each child node, I move it to the location of the parent node, and then want to run .animate() with its original position as the destination so that there is a smooth animation. As seen in the code, the .animate() function doesn't pause the rest of the program. Any advice?
var cy = cytoscape({
container: document.getElementById('cy'),
boxSelectionEnabled: false,
autounselectify: true,
style: cytoscape.stylesheet()
.selector('node')
.css({
'content': 'data(id)'
})
.selector('edge')
.css({
'curve-style': 'bezier',
'target-arrow-shape': 'triangle',
'width': 4,
'line-color': '#ddd',
'target-arrow-color': '#ddd',
}),
elements: {
nodes: [
{ data: { id: 'a' } },
{ data: { id: 'b' } },
{ data: { id: 'c' } },
{ data: { id: 'd' } },
{ data: { id: 'e' } }
],
edges: [
{ data: { id: 'ae', weight: 1, source: 'a', target: 'e' } },
{ data: { id: 'ab', weight: 3, source: 'a', target: 'b' } },
{ data: { id: 'bc', weight: 5, source: 'b', target: 'c' } },
{ data: { id: 'cd', weight: 2, source: 'c', target: 'd' } },
]
},
layout: {
name: 'breadthfirst',
directed: true,
padding: 10,
roots: '#a',
}
}); // cy init
var root_id = 'a'
//get root
var root = cy.$('#'+root_id)[0]
//empty collection
var collection = cy.collection()
//hide all nodes except root
cy.ready(function(event){
collection = collection.add(cy.nodes('node[id!="'+root_id+'"]'))
collection = collection.add(cy.nodes().connectedEdges());
collection.style('visibility', 'hidden')
});
var visited_nodes = [root];
function dfs(node) {
//visit node
visited_nodes.push(node)
//for each neighbor w ov f
neighbors = cy.$('#'+node.id()).outgoers('edge')
neighbors.forEach(function (next) {
var next_node = cy.$('#'+next.data().target)
if (visited_nodes.indexOf(next_node) < 0){
//visit each unvisited node
//we will move the target node to the source node, then use .animate() to move the target node back to it's original location
source_id = next.data('source')
target_id = next.data('target')
var node_to_move = cy.$('#' + next.data('target'))[0]
//record the x and y coordinates to avoid messing around for objects. temporary.
var start_position_x = cy.$('#' + next.data('source')).position().x
var start_position_y = cy.$('#' + next.data('source')).position().y
var end_position_x = cy.$('#' + next.data('target')).position().x
var end_position_y = cy.$('#' + next.data('target')).position().y
//move the target node to its start position
node_to_move.position('x',start_position_x)
node_to_move.position('y',start_position_y)
node_to_move.style('visibility', 'visible')
//then animate the target node moving to it's original position
node_to_move.animate(
{
position: {end_position_x, end_position_y}
},
{
duration: 1000,
complete: function(){
node_to_move.style('visibility', 'visible')
// if (e !== undefined){
// e.style('visibility', 'visible')
// }
}
})
//DOESNT WORK WITH THESE COMMENTED OUT, DOES WITH THEM COMMENTED IN/
//I think this means it is a timing problem, with the dsf moving forward without the nodes getting moved
// node_to_move.position('x',end_position_x)
// node_to_move.position('y',end_position_y)
dfs(next_node)
}
})
}
body {
font: 14px helvetica neue, helvetica, arial, sans-serif;
}
#cy {
height: 100%;
width: 100%;
position: absolute;
left: 0;
top: 0;
}
#eat {
position: absolute;
left: 1em;
top: 1em;
font-size: 1em;
z-index: -1;
color: #c88;
}
<!DOCTYPE html>
<!-- This code is for demonstration purposes only. You should not hotlink to Github, Rawgit, or files from the Cytoscape.js documentation in your production apps. -->
<html>
<head>
<link href="style.css" rel="stylesheet" />
<meta charset=utf-8 />
<meta name="viewport" content="user-scalable=no, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, minimal-ui">
<title>Images</title>
<script src="cube/node_modules/cytoscape/dist/cytoscape.js"></script>
</head>
<body>
<div id="cy"></div>
<!-- Load appplication code at the end to ensure DOM is loaded -->
<script src="expand.js"></script>
</body>
</html>
You ran the animations at the same time. Chain animations using promises, e.g. node1.animation().play().promise().then( node2.animation().play.promise() ).then( ... )

Categories