How to avoid edges overlap in multigraph when using taxi edges? - cytoscape.js

let styleJson = JSON.parse(`
[{
"selector": "node",
"style": {
"label": "data(id)",
"height": "data(height)",
"width": "data(width)",
"text-valign": "center",
"text-halign": "center"
}
}, {
"selector": "node.begin-end",
"style": {
"shape": "ellipse",
"width": "70",
"height": "30"
}
}, {
"selector": "node.if",
"style": {
"shape": "diamond"
}
}, {
"selector": "edge",
"style": {
"width": 2,
"label": "data(id)",
"curve-style": "taxi"
}
}, {
"selector": "edge.true",
"style": {
"taxi-direction": "rightward"
}
}, {
"selector": "edge.false",
"style": {
"taxi-direction": "leftward"
}
}]
`);
let cy = cytoscape({
container: document.getElementById('cy'),
style: styleJson,
elements: {
nodes: [{
data: {
id: 'begin'
},
classes: 'begin-end'
},
{
data: {
id: 'if',
width: "70",
height: "30"
},
classes: 'if'
},
{
data: {
id: 'end',
width: "70",
height: "30"
},
classes: 'begin-end'
}
],
edges: [{
data: {
id: 'n1',
source: 'begin',
target: 'if'
}
},
{
data: {
id: 'n2',
source: 'if',
target: 'end'
},
classes: 'true'
},
{
data: {
id: 'n3',
source: 'if',
target: 'end'
},
classes: 'false'
}
]
}
});
doLayout();
function doLayout() {
let layoutOpts = {
name: 'breadthfirst',
directed: true
};
cy.layout(layoutOpts).run();
}
#cy {
width: 400px;
height: 400px;
display: block;
border: 1px solid black;
margin: 0 auto;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Cytoscape</title>
</head>
<body>
<div id="cy"></div>
<script src="https://cdn.jsdelivr.net/npm/cytoscape#3.17.1/dist/cytoscape.min.js"></script>
</body>
</html>
I just want to split the 2 edges coming from IF node.
The direction is specified (leftward/rightward), but I don't know how to make it working...

Related

How to make a treeview recursive menu in Vue.js but every branch is filled by an API request via Axios?

I need to make a treeview recursive menu in vue.js and i want every branch to be filled with a spefic array collected by an API call with Axios. I found already on the web some examples of this specific menu but i didnt manage put in the data the response from the axios call.
Here there is an example of the aformentioned menu
Menu.vue component
<template>
<div class="menu" :class="{ 'small-menu': smallMenu }">
<MenuItem
v-for="(item, index) in menuTree"
:key="index"
:data="item.children"
:label="item.label"
:icon="item.icon"
:depth="0"
:smallMenu="smallMenu"
/>
<i #click="smallMenu = !smallMenu" class="material-icons">menu</i>
</div>
</template>
<script>
import MenuItem from './MenuItem.vue';
export default {
name: 'recursive-menu',
data: () => ({
smallMenu: false,
menuTree: [
{
label: "Home",
icon: "home",
children: [
{
label: "level 1.1",
children: [
{
label: "level 1.1.1",
children: [
{
label: "level 1.1.1.1"
}
]
}
]
},
{
label: "level 1.2"
}
]
},
{
label: "Dashboard",
icon: "dashboard",
children: [
{
label: "level 2.1",
},
{
label: "level 2.2"
},
{
label: "level 2.3"
}
]
},
{
label: "Settings",
icon: "settings"
}
]
}),
components: {
MenuItem
}
}
</script>
<style lang="scss" scoped>
.menu {
position: fixed;
height: 100vh;
width: 240px;
left: 0;
top: 0;
border-right: 1px solid #ececec;
transition: all .3s ease;
overflow: auto;
i {
position: fixed;
left: 250px;
font-size: 20px;
top: 15px;
user-select: none;
cursor: pointer;
transition: all .3s ease;
}
&.small-menu {
overflow: inherit;
width: 60px;
padding-top: 50px;
i {
left: 20px;
}
}
}
</style>
Here is the response from the API
{
"success": 1,
"numElements": 6,
"data": [
{
"id": "1",
"categoryName": "Fatturato",
"fkParent": null,
"hasChild": "1"
},
{
"id": "2",
"categoryName": "Fatturato Clienti",
"fkParent": "1",
"hasChild": "0"
},
{
"id": "3",
"categoryName": "Fatturato Fornitori",
"fkParent": "1",
"hasChild": "0"
},
{
"id": "4",
"categoryName": "Scaduto",
"fkParent": null,
"hasChild": "1"
},
{
"id": "5",
"categoryName": "Scaduto Clienti",
"fkParent": "4",
"hasChild": "0"
},
{
"id": "6",
"categoryName": "Scaduto Fornitori",
"fkParent": "4",
"hasChild": "0"
}
]
}

cytoscape.js graph, shows/draws only with few nodes and edges

I am creating a graph with Cytoscape.js on my website, to show admins the connections among users. Everything is fine, but some graph are not drawn down. No errors in console, but the canvas seems empty.
For example, a graph that is not drawn has 20 EDGES and 18 NODES.
If I remove some edges, the graph is drawn and showed. No matter to which node or edge I remove: if I reach 18 EDGES it shows. So I thought "maybe it's too large".
But actually I have bigger graphs (37 edges) that are drawn without problems.
What can be the problem?
This is a graph that it's not showing. By the way, is there an online website where I can test this structure? Thanks
{
"elements": {
"nodes": [
{"data": {"id": "1"}},
{"data": {"id": "2"}},
{"data": {"id": "3"}},
{"data": {"id": "4"}},
{"data": {"id": "5"}},
{"data": {"id": "6"}},
{"data": {"id": "7"}},
{"data": {"id": "8"}},
{"data": {"id": "9"}},
{"data": {"id": "10"}},
{"data": {"id": "11"}},
{"data": {"id": "12"}},
{"data": {"id": "13"}},
{"data": {"id": "14"}},
{"data": {"id": "18"}},
{"data": {"id": "15"}},
{"data": {"id": "16"}},
{"data": {"id": "17"}}
],
"edges": [{
"data": {
"source": "1",
"target": "2"
}
}, {
"data": {
"source": "1",
"target": "3"
}
}, {
"data": {
"source": "4",
"target": "3"
}
}, {
"data": {
"source": "5",
"target": "3"
}
}, {
"data": {
"source": "1",
"target": "6"
}
}, {
"data": {
"source": "7",
"target": "6"
}
}, {
"data": {
"source": "1",
"target": "8"
}
}, {
"data": {
"source": "9",
"target": "1"
}
}, {
"data": {
"source": "1",
"target": "9"
}
}, {
"data": {
"source": "1",
"target": "10"
}
}, {
"data": {
"source": "4",
"target": "10"
}
}, {
"data": {
"source": "1",
"target": "11"
}
}, {
"data": {
"source": "1",
"target": "4"
}
}, {
"data": {
"source": "8",
"target": "12"
}
}, {
"data": {
"source": "14",
"target": "13"
}
}, {
"data": {
"source": "8",
"target": "13"
}
}, {
"data": {
"source": "15",
"target": "18"
}
}, {
"data": {
"source": "11",
"target": "18"
}
}, {
"data": {
"source": "11",
"target": "16"
}
}, {
"data": {
"source": "17",
"target": "16"
}
}],
},
}
I got the elements to display just fine:
var cy = window.cy = cytoscape({
container: document.getElementById('cy'),
boxSelectionEnabled: false,
autounselectify: true,
style: [{
selector: 'node',
css: {
'content': 'data(id)',
'text-valign': 'center',
'text-halign': 'center',
'height': '60px',
'width': '60px',
'border-color': 'black',
'border-opacity': '1',
'border-width': '10px'
}
},
{
selector: '$node > node',
css: {
'padding-top': '10px',
'padding-left': '10px',
'padding-bottom': '10px',
'padding-right': '10px',
'text-valign': 'top',
'text-halign': 'center',
'background-color': '#bbb'
}
},
{
selector: 'edge',
css: {
'target-arrow-shape': 'triangle'
}
},
{
selector: ':selected',
css: {
'background-color': 'black',
'line-color': 'black',
'target-arrow-color': 'black',
'source-arrow-color': 'black'
}
}
],
elements: {
nodes: [{
data: {
id: "1"
}
},
{
data: {
id: "2"
}
},
{
data: {
id: "3"
}
},
{
data: {
id: "4"
}
},
{
data: {
id: "5"
}
},
{
data: {
id: "6"
}
},
{
data: {
id: "7"
}
},
{
data: {
id: "8"
}
},
{
data: {
id: "9"
}
},
{
data: {
id: "10"
}
},
{
data: {
id: "11"
}
},
{
data: {
id: "12"
}
},
{
data: {
id: "13"
}
},
{
data: {
id: "14"
}
},
{
data: {
id: "15"
}
},
{
data: {
id: "16"
}
},
{
data: {
id: "17"
}
},
{
data: {
id: "18"
}
}
],
edges: [{
data: {
source: "1",
target: "2"
}
},
{
data: {
source: "1",
target: "3"
}
},
{
data: {
source: "4",
target: "3"
}
},
{
data: {
source: "5",
target: "3"
}
},
{
data: {
source: "1",
target: "6"
}
},
{
data: {
source: "7",
target: "6"
}
},
{
data: {
source: "1",
target: "8"
}
},
{
data: {
source: "9",
target: "1"
}
},
{
data: {
source: "1",
target: "9"
}
},
{
data: {
source: "1",
target: "10"
}
},
{
data: {
source: "4",
target: "10"
}
},
{
data: {
source: "1",
target: "11"
}
},
{
data: {
source: "1",
target: "4"
}
},
{
data: {
source: "8",
target: "12"
}
},
{
data: {
source: "14",
target: "13"
}
},
{
data: {
source: "8",
target: "13"
}
},
{
data: {
source: "15",
target: "18"
}
},
{
data: {
source: "11",
target: "18"
}
},
{
data: {
source: "11",
target: "16"
}
},
{
data: {
source: "17",
target: "16"
}
}
]
},
layout: {
name: 'breadthfirst',
directed: true,
padding: 15
}
});
body {
font: 14px helvetica neue, helvetica, arial, sans-serif;
}
#cy {
height: 100%;
width: 100%;
position: absolute;
left: 0;
top: 0;
float: left;
}
<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://cdnjs.cloudflare.com/ajax/libs/cytoscape/3.2.17/cytoscape.min.js"></script>
</head>
<body>
<div id="cy"></div>
</body>
</html>

Cytoscape.js, position label text on top of edge

I have a label which is painted inline with edge and by this is not rendered well.
Is there a way to add something like background shape to the edge text, so that edge line is not visible at the label area?
You can add classes to labels when initializing the cytoscape stylesheet:
var cy = (window.cy = cytoscape({
container: document.getElementById("cy"),
boxSelectionEnabled: false,
autounselectify: true,
style: [{
selector: "node",
css: {
"label": "data(label)",
"text-valign": "center",
"text-halign": "center",
"height": "60px",
"width": "60px"
}
},
{
selector: "edge",
css: {
"target-arrow-shape": "triangle"
}
},
{
selector: "edge[label]",
css: {
"label": "data(label)",
"text-rotation": "autorotate",
"text-margin-x": "0px",
"text-margin-y": "0px"
}
},
{
selector: ".background",
css: {
"text-background-opacity": 1,
"color": "#fff",
"text-background-color": "#000"
}
},
{
selector: ".outline",
css: {
"color": "#fff",
"text-outline-color": "#000",
"text-outline-width": 3
}
},
{
selector: ".top-center",
style: {
"text-valign": "top",
"text-halign": "center"
}
}
],
elements: {
nodes: [{
data: {
id: '1',
label: 'outline'
},
classes: 'outline'
},
{
data: {
id: '2',
label: 'background'
},
classes: 'background'
},
{
data: {
id: '3',
label: 'top-center'
},
classes: 'top-center'
},
{
data: {
id: '4',
label: 'none'
}
}
],
edges: [{
data: {
source: "1",
target: "2",
label: "outline"
},
classes: "outline"
},
{
data: {
source: "2",
target: "3",
label: "background"
},
classes: "background"
},
{
data: {
source: "3",
target: "4",
label: "top-center"
},
classes: "top-center"
},
{
data: {
source: "4",
target: "1",
label: "none"
}
}
]
},
layout: {
name: "circle"
}
}));
cy.ready(function() {
cy.layout({
name: "circle"
}).run();
});
body {
font: 14px helvetica neue, helvetica, arial, sans-serif;
}
#cy {
height: 100%;
width: 75%;
position: absolute;
left: 0;
top: 0;
float: left;
}
<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://cdnjs.cloudflare.com/ajax/libs/cytoscape/3.2.17/cytoscape.min.js"></script>
<script src="https://unpkg.com/jquery#3.3.1/dist/jquery.js"></script>
</head>
<body>
<div id="cy"></div>
</body>
</html>

Particles.js into Vue component

I'm trying to adapt particles.js to a Vue.js component from the example below:
https://codepen.io/MichaelVanDenBerg/pen/WpXGRm
However, when using the code below, I get the following error message in the console:
[Vue warn]: Error in mounted hook: "TypeError: this.particles is not a
function"
How can I fix it in the code below?
Template:
</template>
<div>
<div id="particles-js"></div>
</div>
</template>
Script:
<script>
import particles from 'particles.js'
export default {
mounted() {
this.initParticles()
},
methods: {
initParticles () {
particles("particles-js", {
"particles": {
"number": {
"value": 80,
"density": {
"enable": true,
"value_area": 700
}
},
"color": {
"value": "#ffffff"
},
"shape": {
"type": "circle",
"stroke": {
"width": 0,
"color": "#000000"
},
"polygon": {
"nb_sides": 5
},
},
"opacity": {
"value": 0.5,
"random": false,
"anim": {
"enable": false,
"speed": 1,
"opacity_min": 0.1,
"sync": false
}
},
"size": {
"value": 3,
"random": true,
"anim": {
"enable": false,
"speed": 40,
"size_min": 0.1,
"sync": false
}
},
"line_linked": {
"enable": true,
"distance": 150,
"color": "#ffffff",
"opacity": 0.4,
"width": 1
},
"move": {
"enable": true,
"speed": 6,
"direction": "none",
"random": false,
"straight": false,
"out_mode": "out",
"bounce": false,
"attract": {
"enable": false,
"rotateX": 600,
"rotateY": 1200
}
}
},
"interactivity": {
"detect_on": "canvas",
"events": {
"onhover": {
"enable": true,
"mode": "grab"
},
"onclick": {
"enable": true,
"mode": "push"
},
"resize": true
},
"modes": {
"grab": {
"distance": 140,
"line_linked": {
"opacity": 1
}
},
"bubble": {
"distance": 400,
"size": 40,
"duration": 2,
"opacity": 8,
"speed": 3
},
"repulse": {
"distance": 200,
"duration": 0.4
},
"push": {
"particles_nb": 4
},
"remove": {
"particles_nb": 2
}
}
},
"retina_detect": true
})
}
}
}
</script>
CSS:
<style scoped>
#particles-js {
position: absolute;
width: 100%;
height: 100%;
background: #00356B;
}
</style>
The particles.js package does not export anything, but rather it sets window.particlesJS.
To use this package, simply import it in your script, and then invoke particlesJS():
import 'particles.js'
export default {
// ...
methods: {
initParticles() {
window.particlesJS('particles-js', {/* ... */})
}
}
}
demo

Put nodes on the border of the parent in cytoscape

I want to put the child nodes on the very border of the parent node (evenly distributed if possible) instead of inside the parent node.
Can this be achieved? Assuming that I could somehow get the position and size of the parent and set the position of the children based on this?
Still I would like the edges to be as short as possible, and as I add more nodes and edges it will be a mess to try to set coordinates myself
So it would be best if the layout algorithm did the job of distributing the nodes, just want them moved out on the border.
<style>
body {
font-family: helvetica;
font-size: 14px;
}
#cy {
width: 100%;
height: 100%;
position: absolute;
left: 0;
top: 0;
z-index: 999;
}
h1 {
opacity: 0.5;
font-size: 1em;
}
</style>
<script>
$(function(){
var cy = window.cy = cytoscape({
container: document.getElementById('cy'),
layout: {
name: 'cose-bilkent'
},
style: [
{
selector: 'node',
style: {
shape: 'roundrectangle',
width: 10,
height: 10,
'background-color': '#ad1a66',
label: function (ele) { return (ele.data('id')) }
}
},
{
selector: ':parent',
style: {
width: 80,
height: 120,
'background-opacity': 0.333
}
},
{
selector: 'edge',
style: {
'width': 3,
'line-color': '#ad1a66'
}
}
],
elements: [
{ "data": { "id": "n2" }},
{ "data": { "id": "p6", "parent": "n2" }},
{ "data": { "id": "p1", "parent": "n2" }},
{ "data": { "id": "p4", "parent": "n2" }},
{ "data": { "id": "p7", "parent": "n2" }},
{ "data": { "id": "n3" }},
{ "data": { "id": "p2", "parent": "n3" }},
{ "data": { "id": "p5", "parent": "n3" }},
{ "data": { "id": "p8", "parent": "n3" }},
{ "data": { "id": "e46", "source": "p7", "target": "p2" }, "position": {}},
]
});
});
</script>
A compound parent node does not have an independent position or size. It just fits to its children.
So, you can put a child node at the parent's edge -- but not beyond it -- by positioning the child relative to its siblings. Make sure to set the padding to zero on the parent.
Force-directed layouts will try to minimise the area taken up by each parent node, as this is a highly desirable property. This means that for groups of three or more siblings a layout will tend to put nodes in the middle.
Your criterion is at odds with the general goal of compound force-directed layout, so you could try setting the repulsion forces really high or you could write an extension with your own custom logic.