Put nodes on the border of the parent in cytoscape - cytoscape.js

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.

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"
}
]
}

How to avoid edges overlap in multigraph when using taxi edges?

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...

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>

How can I extend the x-axis on a Highcharts graph?

I have a Highcharts bar graph. Each point has a group of results, however the first and last element are being cropped. How can I extend the x-axis so every bar is shown?
In the image below each group has the same results so you can see the N and P are dropped from the first group and S and Mg from the last grouping.
The data is coming from a database, so i don't know how many groups there will be, or what range (so simply adding a day to each end is not sufficient, the overlap could be larger or smaller)
const conf = {
chart: {
type: "column",
animation: false,
marginRight: 10,
dateFormat: "dd/mm/YYYY"
},
title: {
text: "Spread Events"
},
xAxis: {
type: "datetime",
tickPixelInterval: 50
},
yAxis: {
title: {
text: "Spread"
},
plotLines: [
{
value: 0,
width: 1,
color: "#808080"
}
]
},
legend: {
enabled: true
},
exporting: {
enabled: false
},
plotOptions: {
column: {
pointPadding: 0.2,
borderWidth: 0
}
},
series: this.state.graphData
};
and this is the graphData from the example
[
{
"name": "N",
"data": [[1559669642443, 300], [1559343600000, 300], [1559257200000, 300]]
},
{
"name": "P",
"data": [[1559669642443, 160], [1559343600000, 160], [1559257200000, 160]]
},
{
"name": "K",
"data": [[1559669642443, 470], [1559343600000, 470], [1559257200000, 470]]
},
{
"name": "S",
"data": [[1559669642443, 120], [1559343600000, 120], [1559257200000, 120]]
},
{
"name": "Mg",
"data": [[1559669642443, 90], [1559343600000, 90], [1559257200000, 90]]
}
]
You have Highcharts error #15 in a console, which means that your data is not sorted. Highcharts requires sorted data in ascending X order:
series: [{
...,
data: [
[1559257200000, 300],
[1559343600000, 300],
[1559669642443, 300]
]
}, ...
]
Live demo: http://jsfiddle.net/BlackLabel/y2rzd65m/

ExtJS 4 and grid questions

I can't find proper answer for two questions concerning grid (lot of different answers, no one good):
how can I center texts in grid cells vertically;
how can I display vertical lines as well as horizontal.
Be so kind as to prompt me, please.
Apply custom css to your grid. Something like this:
#GridId div.x-grid-cell-inner { text-align: center; }
To center the text in the center of the column use the align property, this way align: 'center'. Notice that the email column is centralized.
In the case of vertical lines it is more complicated.
ExtJS has a setting to show the lines of the column columnLines: true, but doesn’t have a property to show line rows. So we should use a CSS class and set the bottom border, in this way border-bottom-color: red !important;
To test in other versions of ExtJS access this fiddle.
var Grid1Store = new Ext.data.JsonStore({
fields: ['id', 'name', 'email'],
autoLoad: true,
data: [{
"id": 1,
"name": "John Smith",
"email": "jsmith#example.com"
}, {
"id": 2,
"name": "Anna Smith",
"email": "asmith#example.com"
}, {
"id": 3,
"name": "Peter Smith",
"email": "psmith#example.com"
}, {
"id": 4,
"name": "Tom Smith",
"email": "tsmith#example.com"
}, {
"id": 5,
"name": "Andy Smith",
"email": "asmith#example.com"
}, {
"id": 6,
"name": "Nick Smith",
"email": "nsmith#example.com"
}]
});
var onReadyFunction = function() {
var grid = new Ext.grid.GridPanel({
renderTo: Ext.getBody(),
frame: true,
title: 'Example',
width: 450,
height: 200,
store: Grid1Store,
columns: [{
header: "Id",
dataIndex: 'id',
width: '50px'
}, {
header: "Name",
dataIndex: 'name',
width: '150px'
}, {
header: "Email",
dataIndex: 'email',
width: '200px',
align: 'center'
}]
});
}
Ext.onReady(onReadyFunction);
.x-grid-cell {
border-bottom-color: red !important;
}
<script src="http://cdn.sencha.com/ext/gpl/4.2.0/ext-all.js"></script>
<link type="text/css" rel="stylesheet" href="http://cdn.sencha.com/ext/gpl/4.2.0/resources/css/ext-all.css")/>