cytoscape.js: How to prevent adjacent taxi edges from overlapping - cytoscape.js

I'm attempting to use a Dagre layout and taxi edges in cytoscape.js to show how nodes connect to other nodes. However, in some cases, due to how the corners of the edges can overlap, it appears that a node has multiple parents, where it actually has only one.
I've tried changing some of the taxi style settings for the edges, but since those are applied to all edges, I still experience the same issue (just in a different spot). Is there a configuration setting I'm missing that will prevent these unrelated edges from overlapping? Barring that, is there a way to change, say, the taxi-turn setting for only some edges? (To make it more complex, this will need to work with arbitrary nodes/edges provided by a user, so I'm unable to handle specific, one-off issues.)
I've reproduced the issue here: https://stackblitz.com/edit/react-rj7ln4?file=index.js
Specifically, the edges in question are from the last two nodes in the first column.
Styles, including configuration for taxi settings
const styles = [
// disable the visible selector when clicking to pan
{
selector: "core",
style: {
"active-bg-opacity": 0
}
},
// basic styling for nodes
{
selector: "node",
style: {
"background-clip": "none",
shape: "rectangle",
"background-opacity": 0,
"font-family": "serif"
}
},
// disable overlay when clicking on nodes or edges
{
selector: "node, edge",
style: {
"overlay-opacity": 0.1
}
},
// basic styles for edges
{
selector: "edge, edge:selected",
style: {
"line-color": "rgb(143,156,173)",
"curve-style": "taxi",
"edge-distances": "node-position",
"taxi-direction": "horizontal",
"taxi-turn": "75%",
"taxi-turn-min-distance": "30px",
"target-endpoint": "outside-to-node",
"target-arrow-shape": "triangle",
"target-arrow-color": "rgb(143,156,173)",
"target-distance-from-node": 15
}
},
// hide the boundary around compound nodes
{
selector: "node:parent",
style: {
opacity: 0.91
}
}
];
Array of nodes and edges used to reproduce
const elements = [
{
data: {
id: "A",
title: "A"
}
},
{
data: {
id: "B",
title: "B"
}
},
{
data: {
id: "C",
title: "C"
}
},
{
data: {
id: "D",
title: "D"
}
},
{
data: {
id: "E",
title: "E"
}
},
{
data: {
id: "F",
title: "F"
}
},
{
data: {
id: "AD",
source: "A",
target: "D"
}
},
{
data: {
id: "BD",
source: "B",
target: "D"
}
},
{
data: {
id: "CE",
source: "C",
target: "E"
}
},
{
data: {
id: "DF",
source: "D",
target: "F"
}
},
{
data: {
id: "EF",
source: "E",
target: "F"
}
}
];
Cytoscape.js version: 3.10.0
Browsers & versions: Firefox 70.0.1 / Chrome 78.0.3904.97 on macOS 10.14.6

Related

Define (and preserve) compound node shape in Cytoscape, allowing rotation

Is it possible to specify a shape (i.e. square, or triangle) of a compound node in Cytoscape, which will be preserved when the layout of the graph is computed/rendered? The compound nodes should be rotated freely, as needed to give a good/simple layout.
As an example, consider the graph below with three compound nodes, I - III. Compound nodes II and III could be rotated to give a simple layout. Importantly, the internal layout of compound nodes is not affected (I remains a triangle, and the other two remain linear).
Is is possible to define such a graph, and get a similar output, using Cytoscape.js?
Or maybe a slightly more complex example where all three nodes are rotated
Here's what I've tried so far:
var cy = cytoscape({
container: document.getElementById('cy'),
elements: [
// nodes
{ data: { id: 'IA', parent: 'I' } },
{ data: { id: 'IB', parent: 'I' } },
{ data: { id: 'IC', parent: 'I' } },
{ data: { id: 'I' } },
{ data: { id: 'IIA', parent: 'II' } },
{ data: { id: 'IIB', parent: 'II' } },
{ data: { id: 'IIC', parent: 'II' } },
{ data: { id: 'II' } },
{ data: { id: 'IIIA', parent: 'III' } },
{ data: { id: 'IIIB', parent: 'III' } },
{ data: { id: 'III' } },
// edges
{
data: {
id: '1',
source: 'IA',
target: 'IIA'
}
},
{
data: {
id: '2',
source: 'IC',
target: 'IIB'
}
},
{
data: {
id: '3',
source: 'IC',
target: 'IIIA'
}
},
{
data: {
id: '4',
source: 'IB',
target: 'IIIB'
}
},
],
layout: {
name: 'grid'
},
style: [
{
selector: 'node',
style: {
label: 'data(id)'
}
}]
});
cy.layout({
name: 'fcose'
}).run();
which gives this

Keep edges seperated

I have created the following diagram using ELK.
Now I'm trying to use cytoscape.js with cytoscape.js-elk to generate the same routing. However I cannot get the edges the same was with ELK.
I keep getting the following:
What do I need to do to get the same edge route as ELK generates? I have tried changing the ELK options, but I don't think this is caused by elk, I think this is caused by cytoscape.
My style
const style = [ // the stylesheet for the graph
{
selector: 'node',
style: {
shape: 'rectangle',
label: 'data(id)',
'font-size': '0.5em',
}
},
{
selector: 'edge',
style: {
'width': 1,
'curve-style': 'taxi',
'line-color': '#ccc',
label: 'data(id)',
'font-size': '0.5em',
'color': 'blue',
'target-arrow-color': '#ccc',
'target-arrow-shape': 'triangle'
}
}
];
Settings
cytoscape.use(elk);
var cy = cytoscape({
container: document.getElementById('cy'),
style: style,
layout: { name: 'elk' }
});
Nodes & Edges
var nodes = cy.add([
{ group: 'nodes', data: { id: 'n1'} },
{ group: 'nodes', data: { id: 'n2'} },
{ group: 'nodes', data: { id: 'n4'} },
{ group: 'nodes', data: { id: 'n9'} },
{ group: 'nodes', data: { id: 'n10' } },
{ group: 'nodes', data: { id: 'n555'} },
{ group: 'nodes', data: { id: 'n556'} },
{ group: 'nodes', data: { id: 'n557'} },
])
var edges = cy.add([
{ group: 'edges', data: { id: 'e3', source: 'n1', target: 'n4' } },
{ group: 'edges', data: { id: 'e19', source: 'n1', target: 'n9' } },
{ group: 'edges', data: { id: 'e12', source: 'n2', target: 'n1' } },
{ group: 'edges', data: { id: 'e842', source: 'n2', target: 'n9' } },
{ group: 'edges', data: { id: 'e15', source: 'n2', target: 'n10' } },
{ group: 'edges', data: { id: 'e10', source: 'n4', target: 'n2' } },
{ group: 'edges', data: { id: 'e908', source: 'n4', target: 'n4' } },
{ group: 'edges', data: { id: 'e843', source: 'n4', target: 'n9' } },
{ group: 'edges', data: { id: 'e11', source: 'n4', target: 'n10' } },
{ group: 'edges', data: { id: 'e905', source: 'n4', target: 'n555' } },
{ group: 'edges', data: { id: 'e862', source: 'n4', target: 'n557' } },
{ group: 'edges', data: { id: 'e802', source: 'n9', target: 'n1' } },
{ group: 'edges', data: { id: 'e7', source: 'n10', target: 'n2' } },
{ group: 'edges', data: { id: 'e8', source: 'n10', target: 'n4' } },
]);
Options
var options = {
name: "elk",
nodeDimensionsIncludeLabels: true, // Boolean which changes whether label dimensions are included when calculating node dimensions
fit: true, // Whether to fit
padding: 20, // Padding on fit
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
transform: function (node, pos) { return pos; }, // A function that applies a transform to the final node position
ready: undefined, // Callback on layoutready
stop: undefined, // Callback on layoutstop
elk: {
'algorithm': 'layered',
'layered.mergeEdges': 'false',
'layered.mergeHierarchyEdges': 'false',
'crossingMinimization.semiInteractive': true,
'nodePlacement.strategy': 'NETWORK_SIMPLEX',
'layered.wrapping.additionalEdgeSpacing': 50,
'spacing.nodeNode': 50,
'spacing.nodeNodeBetweenLayers': 25,
'spacing.edgeNode': 25,
'spacing.edgeNodeBetweenLayers': 20,
'spacing.edgeEdge': 20,
'spacing.edgeEdgeBetweenLayers': 15,
// All options are available at http://www.eclipse.org/elk/reference.html
// 'org.eclipse.elk.' can be dropped from the Identifier
// Or look at demo-demo.js for an example.
// Enums use the name of the enum e.g.
// 'searchOrder': 'DFS'
//
// The main field to set is `algorithm`, which controls which particular
// layout algorithm is used.
},
priority: function (edge) { return null; }, // Edges with a non-nil value are skipped when geedy edge cycle breaking is enabled
};
cy.layout(options).run();
I hit the same problem. Another you will encounter is that the lines will go through the nodes, there is no effort to route around them. For me this has rendered taxi useless, even though I much prefer it's appearance. I am not aware of any line drawing algorithm in cytoscape that fixes either of these issues.
My reluctant conclusion is that I need to switch platforms, and transfer entirely to Sprotty and ELK (which was probably used to draw your reference graph).
https://rtsys.informatik.uni-kiel.de/elklive/examples.html?e=general%2FmixingDirection
https://www.typefox.io/blog/sprotty-a-web-based-diagramming-framework
Examples with Sprotty show lines actually routing around nodes, and junction points on nodes to show where overlapping lines actually join. Additionally it uses SVGs rather than a raster-render system. This may result in a crisper font, but also has the advantage of searchable text in your diagram (if you care about that).
I have yet to take the plunge on yet another framework, but it looks good.
I am also facing similar kind of problem where my edges are overlapping to each other. To overcome this problem you can use curve-style: straight;.
Edges type demo

Cytoscape.js setting node colour and other details

I am using Cytoscape.js to create a basic diagram and am curious as to how to set the colour and particular shape of each node. Here is my existing code:
layout: {
name: 'preset'
},
ready: function(){
window.cy = this;
cy.add([
{ group: "nodes", data: { id: "n0" }, position: { x: 100, y: 100 } },
{ group: "nodes", data: { id: "n1", shape: 'rectangle' }, position: { x: 200, y: 200 } },
{ group: "edges", data: { id: "e0", source: "n0", target: "n1" } }
]);
}
});
});
I was also curious as to how to animate the diagram and if there were any similar examples out there on that.
cheers,
...
style: [ // the stylesheet for the graph
{
selector: 'node',
style: {
'background-color': '#666',
'shape': 'rectangle',
}
},
],
...
background-color is probably the attribute you are looking for, this is set in the style option. More info can be in http://js.cytoscape.org/#getting-started/specifying-basic-options.
As for the shape, cytoscape has a few predefined shapes such as rectangle, circle, etc. You can even make your own, or have an svg as the node's image. Options for nodes are listed in http://js.cytoscape.org/#style/node-body.
For animation I would look at some of the demos uploaded and go from there. This one is particularly interesting js.cytoscape.org/demos/aedff159b0df05ccfaa5

Need to use Remote Kendo Grouped and Stacked Bar Chart

I am using Kendo Bar Chart, which will show like grouped and stacked for dynamic data. In category field i need to display usernames and it has to show respective metric values, here metric values are dynamic and variable, and metric count values are displayed in bar, which is like stacked bar.
For Example:
Metric values are - High, Low, Medium, ...
Metric values count -> High-4, Low-2, and medium -5 ,..
Here counts 4,2, and 5 to be shown in a stacked bar.
Like this i have to show for multiple users with respective the values.
Below i have shown my Code, may i know how to use dynamic stacked bar kendo chart, and can i have solution for above mentioned problem.
Code- Chart Definition
$("#chartForWorkItems").kendoChart({
title: {
text: "Bugs State Analysis"
},
//defining the datasource for loading the chart
dataSource: {
transport: {
read: {
url: baseUri + "ProjectReportWorkItemChart/chartDisplayForWorkITems",
cache: false,
type: "POST",
dataType: "json",
complete: function (e) {
},
},
parameterMap: function (options, operation) {
console.log(selectedCategoryByMetrics);
if (operation == "read") {
return {
workItems: selectedCategoryByMetrics,
Projectid: sessionStorage.getItem("prjprojectid"),
metricsWithWorkItem: metricsWithWorkItem,
users: usersSelected
}
}
},
},
schema: {
model: {
id: "field",
fields: {
field: { type: "string" },
count_f: { type: "string" },
metric: { type: "string" },
assignedTo: { type: "string" },
}
}
}
},
chartArea: {
width: 400,
height: 300,
},
seriesDefaults: {
type: "column",
},
series: [{
field: "count_f",
},
],
categoryAxis: {
categories: "assignedTo",
field: "field",
majorGridLines: {
visible: true
}
},
valueAxis: [{ //display count in the value axis
line: {
visible: true
},
majorGridLines: {
visible: true
}
}],
tooltip: { //To display tool tip
visible: true
},
});

Refreshing a rallychart

I have a Rally SDK 2.0p5 app that displays a chart. When the user selects an option, the data will be updated and I would like to refresh the chart. But instead of redrawing, it will place a new chart beneath. What is the proper syntax?
// Configure and Add the chart
this.add(
{
xtype: 'rallychart',
height: 400,
id: 'chart',
chartConfig: {
chart: {
},
title: {
text: 'My Chart',
align: 'center'
},
xAxis: [
{
categories: ['M0','M1','M2','M3','M4','M5'],
title: {
text: 'Interval'
}
}
],
yAxis: {
title: {
text: yText
}
},
series: [ { type: 'column',
name: yText,
data: mCount } ],
plotOptions : {
column: {
color: '#F00'
},
series : {
animation : {
duration : 2000,
easing : 'swing'
}
}
}
}
}
);
You need to remove the 1st chart before adding the new one.
redrawChart: function() {
this.remove('#chart');
this.add({...});
}
It's often better to just update the chart in place. HighCharts is the charting library that's included in the App SDK. HighCharts will even animate your changes. Cool!!! Look here for the list of methods that operate on charts. You can add series, change data, change axis limits, control the zoom, etc.