cytoscape.js redundant edges - javascript

cytoscape.js to fails to render the graph if the graph has redundant edges/arcs. Why is this happening?
Example:
https://jsfiddle.net/smiccke/mq5t1rw9/4/
$(function() {
var cy = window.cy = cytoscape({
container: document.getElementById('cy'),
ready: function() {},
style: [{
selector: 'node',
css: {
'content': 'data(name)'
}
}, {
selector: 'edge',
css: {
'target-arrow-shape': 'triangle'
}
}],
elements: {
nodes: [{
data: {
id: 'j',
name: 'Jerry'
}
}, {
data: {
id: 'e',
name: 'Elaine'
}
}, {
data: {
id: 'k',
name: 'Kramer'
}
},
],
edges: [{
data: {
source: 'j',
target: 'e'
}
}, {
data: {
source: 'j',
target: 'k'
}
}
]
}
});
});
The graph works if you remove the two redundant edges from the end (j->e, j->e).
It seems like one redundant edge is ok, but two or more is a problem. Any clues why this is so?
As workaround, any nice short-cuts to remove redundant edges from the graph?

All the edges are there. You've rendered all parallel edges on top of one another, because that's how your edge style is specified.
Use appropriate style for the type of graph you're rendering. E.g., a multigraph should probably use haystack edges with a non-zero haystack radius or bundled bezier edges.
Refer to curve-style etc. in the docs: http://js.cytoscape.org/#style/edge-line

Edit: you mention that cytoscape.js fails to render the graph, but it renders fine for me. Are you truly not seeing any graph at all? If so, what browser are you using, and have you checked your code for errors?
Multiple edges between nodes default along the same path as maxkfranz said. You can set the 'curve-style': 'bezier' which will show all of the edges, or use 'haystack-radius': 1 to keep straight lines (play with values between 0 and 1).
I also noticed you have directed arrows turned on. These won't work with the default 'haystack' curve style, so I would suggest you use 'bezier' if you want to visualize directed edges.

Related

HighStock: chart gets broken when navigator touches right border

I'm developing an web application that handles and shows large amounts of live data from some devices. To visualise the data I decided to use HighStock. It seems to work well on most of the data:
However, when the bottom navigator touches right border, the picture becomes quite different:
The timeline is almost the same, but the number of points is different, also vertical scale is different... What is this happening? How to fix it?
My code looks this way:
const ch1 = Highcharts.stockChart('chart1', {
rangeSelector: {
selected: 1,
inputEnabled: false,
buttonTheme: {visibility: 'hidden'},
labelStyle: {visibility: 'hidden'},
},
title: {
text: 'Metrics',
},
series: [{
name: 'Sensor 1', data: [],
}, {
name: 'Sensor 2', data: [],
}, {
name: 'Sensor 3', data: [],
}]
});
// a,b,c gets values from the server
// They are arrays of pairs of timestamp & value
ch1.series[0].setData(a);
ch1.series[1].setData(b);
ch1.series[2].setData(c);
// tm_min & tm_max are dynamically calculated using the data
ch1.xAxis[0].setExtremes(tm_min, tm_max);
Update: Here is an example with 2% of my data – try to do the same as shown above.
I found the solution. The issue is caused by your data and xAxis.ordinal that is enabled by default in Highstock. Your data has many empty points on the right side of the chart and because of ordinal, the empty space was not rendered, yet dataGrouping grouped data differently.
Check this here https://jsfiddle.net/BlackLabel/x1tgqbw6/ (disabled ordinal):
xAxis: {
ordinal: true
}
So, the solution is to disable xAxis.ordinal or generate your data without null points:
https://jsfiddle.net/BlackLabel/ex054oy8/
API reference:
https://api.highcharts.com/highstock/xAxis.ordinal

Why does Cytoscape go into an "infinite" loop in this specific situation?

If I clone a layout configuration with Object.assign and try to use that layout, cytoscape quickly causes an out of memory error. I can work around this by just defining a second layout mostly identical to the first and not cloning, but I am interested to know the reason behind the problem, or if it's a possible bug in cytoscape.
With this example code, click add and layout 2 right after loading the page and it will hang/run out of memory. (Have your task manager handy to kill your tab or browser.) Different combinations of adding nodes and running the cloned layout will mostly hang, but not always.
let cy
const layout1 = {
name: 'euler',
springLength: edge => 80,
mass: node => 4,
randomize: true,
animate: false,
gravity: -1.2,
maxIterations: 1000,
maxSimulationTime: 4000,
}
const layout2 = Object.assign({}, layout1, {
fit: false,
animate: true,
randomize: false,
})
document.addEventListener('DOMContentLoaded', function() {
cy = cytoscape({
container: document.getElementById('cy'),
layout: layout1,
style: [
{
selector: 'node',
style: {
label: 'data(id)',
},
},
],
elements: [
{ data: { id: 'a' } },
{ data: { id: 'b' } },
{ data: { id: 'a_b', source: 'a', target: 'b' } },
],
})
})
function add() {
cy.add([
{ data: { id: 'c' } },
{ data: { id: 'd' } },
{ data: { id: 'c_d', source: 'c', target: 'd' } },
])
// cy.layout(layout2).run()
}
function doLayout1() {
cy.layout(layout1).run()
}
function doLayout2() {
cy.layout(layout2).run()
}
function addAndLayout2() {
add()
doLayout2()
}
<!DOCTYPE html>
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/cytoscape/3.5.0/cytoscape.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/cytoscape-euler#1.2.1/cytoscape-euler.min.js"></script>
</head>
<style>
body {
height: 100%;
}
#cy {
height: 100%;
flex-grow: 1;
}
.main {
height: 100vh;
display: flex;
flex: 1;
}
</style>
<body>
<button onclick="add()">add nodes</button>
<button onclick="doLayout1()">layout 1</button>
<button onclick="doLayout2()">layout 2</button>
<button onclick="addAndLayout2()">add and layout 2</button>
<div class="main">
<div id="cy"></div>
</div>
</body>
</html>
This has nothing to do with Ojbect.assign (even if you did not copy the object properly, it should not hang).
The reason is the randomize option. For this particular graph, when the randomize option is set to false, the layout never ends. Just remove randomize: false from the second layout, or after adding the new nodes and before running layout2, run the random layout (or just randomize the nodes manually) -- the layout2 will terminate.
The problem is that: the layout must terminate at some point (in the worst case when the umber of max iterations is reached). But this particular layout never terminates.
The interesting thing is that this simple graph turns out to be one of the worst cases for some other layout algorithms as well (for randomized: false). I tried cose-bilkent. It also takes a little bit longer and terminates when the maximum number of iterations is reached (setting numIter option to lower number will result in early termination, worse quality) -- but the result is really bad.

C3 and React - DOMException

I'm trying to use C3 to render a chart in my React project.
The problem
What I do is dynamically generate an id (UUID) and attach it to a div in my chart component. I then call some code after the component has rendered in componentDidMount. This is a common pattern, I have seen it in other projects.
At first, everything seemed fine. However, after repeatedly refreshing the page, sometimes the chart generation does not work. The error I get is:
DOMException: Failed to execute 'querySelector' on 'Document': '#a-really-long-id' is not a valid selector.
What I tried
I tried using setTimeout to delay when the chart was attached, but curiously I still got the same result, even after a 10 second delay. This leads me to believe that this is not a race condition, and caused by something else. Maybe C3 reads the DOM once and does not respond to changes? But that would not explain why it works sometimes...
Even trying to select the element by id from the Chrome developer console did not work.
Code
Here is my full component code:
// assets/js/index.jsx
import React from 'react';
import uuid from 'uuid/v4';
import c3 from 'c3';
class Chart extends React.Component {
constructor(props) {
super(props);
this.id = uuid();
this._update = this._update.bind(this);
}
_update() {
const data = this.props.data;
this.chart = c3.generate({
bindto: `#${this.id}`,
data: {
columns: [
['Male', 0],
['Female', 0],
['Brand', 0]
],
type: 'bar',
colors: {
Male: '#0066CC',
Female: '#FF6666',
Brand: '#808080'
},
groups: [
['Male', 'Female', 'Brand']
],
order: null
},
bar: {
width: {
ratio: 0.3
}
},
transition: {
duration: 500
},
tooltip: {
show: false
},
axis: {
x: { show: false },
y: { show: false },
},
size: { width: 220, height: 320 },
grid: {
x: { show: false }
},
legend: {
show: false
}
});
setTimeout(function() {
this.chart.load({
columns: [
['Male', data.male],
['Female', data.female],
['Brand', data.brand]
]
});
}, 500);
}
componentDidMount() {
this._update();
}
render() {
return (
<div id={this.id} className="chart"></div>
);
}
}
export default Chart;
This can be added as a comment, but since its big, adding it as an answer.
Are you using html4/ html5 semantics?
As per HTML4 (https://www.w3.org/TR/html401/types.html)
ID and NAME tokens must begin with a letter ([A-Za-z]) and may be
followed by any number of letters, digits ([0-9]), hyphens ("-"),
underscores ("_"), colons (":"), and periods (".").
As per HTML5 (https://www.w3.org/TR/html5/dom.html)
When specified on HTML elements, the id attribute value must be unique
amongst all the IDs in the element’s tree and must contain at least
one character. The value must not contain any space characters.
Your uuid might be generating a valid Id sometimes and sometimes it might not (not sure how uuid works)
If you are not using HTML5 semantics, you can simply add <!DOCTYPE html>
at the top of your html document and give a try.
Also,
You used settimeout in your logic and within that you used this.Chart
this, now will refer to settimeout rather than the class.
Can you try changing
setTimeout(function() {
this.chart.load({
columns: [
['Male', data.male],
['Female', data.female],
['Brand', data.brand]
]
});
}, 500);
to
setTimeout( () => {
this.chart.load({
columns: [
['Male', data.male],
['Female', data.female],
['Brand', data.brand]
]
});
}, 500);

Using Cytoscape.js in REPL environment

I'm trying to explore the cytoscape graph core object and I want to access its properties in runtime. Can I use Node.js interpreter to instantiate the cy object and run methods on the elements ? If this is an option, I also don't understand where 're real graphics going to be displayed. Is Node.js will open a browser window ?
Node.js REPL represents JavaScript interpreter, but it has no relation to DOM. From the examples on how to use cytoscape, the DOM is required:
var cy = cytoscape({
container: document.getElementById('cy') // container to render in
});
So it seems you can't use cytoscape's visual features with REPL. However, the docs says that:
container : A HTML DOM element in which the graph should be rendered.
This is unspecified if Cytoscape.js is run headlessly.
But I think you can use REPL to run Cytoscape headlessly.
Actually i just find how to run Cytoscape in a REPL environment. still didnt find a way to display it graphically, but i can interact with the object to explore its properties :
$ node
>var cytoscape = require('cytoscape');
>var cy = cytoscape({
container: document.getElementById('cy'), // container to render in
elements: [ // list of graph elements to start with
{ // node a
data: { id: 'a' }
},
{ // node b
data: { id: 'b' }
},
{ // edge ab
data: { id: 'ab', source: 'a', target: 'b' }
}
],
style: [ // the stylesheet for the graph
{
selector: 'node',
style: {
'background-color': '#666',
'label': 'data(id)'
}
},
{
selector: 'edge',
style: {
'width': 3,
'line-color': '#ccc',
'target-arrow-color': '#ccc',
'target-arrow-shape': 'triangle'
}
}
],
layout: {
name: 'grid',
rows: 1
}
});
After i instantiate the cy object, i can interact with it by typing :
> cy.
cy.__defineGetter__ cy.__defineSetter__
cy.__lookupGetter__ cy.__lookupSetter__
cy.__proto__ cy.constructor
cy.hasOwnProperty cy.isPrototypeOf
cy.propertyIsEnumerable cy.toLocaleString
cy.toString cy.valueOf
> cy.elements().forEach(function(e){ console.log(e.data())});
{ id: 'a' }
{ id: 'b' }
{ id: 'ab', source: 'a', target: 'b' }

increase node size vis.js

I have a problem with vis.js, I cannot increase the size of a node and the font of its label.
nodes.push({id: el, label: currentNode.hostName,font: {color:'#FF9900', face:'courier',size:300}, group: "Domain", color:{background: getColor(currentNode) , border: getColor(currentNode) }, value: 10000, title: label,labelHighlightBold: true, borderWidthSelected: 7 });
Domain: {
shape: 'triangleDown',
shapeProperties: {
useImageSize: false,
size:3333
}
}
Thank you
you can define the font size inside the option for creating the network
var options = {
nodes : {
shape : 'dot',
size : 15,
color : '#ECBF26', // select color
font : {
size : 16,
color : '#ffffff'
},
borderWidth : 2
},
Well, I know its a little late, but I just started working with viz.js so you can do it like this:
Assuming you have your nodes save in NODES
NODES.update({ id: yourNodeId, size: 100 });
With the same method you can pretty much change anything (except for theid, which I havent been able to change like this yet) on your nodes or edges.
You can find that here, although I must admit that I ve seen better documentation for a library.
http://visjs.org/docs/data/dataset.html

Categories