Avoiding overlapping edges with minimal physics in vis.js - javascript

I'm using vis.js to build a story visualization tool, and a key feature is to allow authors to manually position nodes via dragging. There's also often several edges with the same origin and destination nodes. Without physics, these edges overlap.
Currently to avoid edge overlap, I enable physics for a small interval whenever a new edge is created to repel any overlapping edges from each other. Ideally I would have physics always disabled and edges would not overlap, but I don't think that is possible.
Are there any recommendations for how to apply vis physics so that it's disabled on node drag, stabilizes quickly also prevents edge overlap?

If anyone comes across this issue, the solution is to calculate the roundness for each edge, based on how many edges have the same origin and destination node.
example: http://jsbin.com/wojanuboxi/edit?html,js,output
var nodes = new vis.DataSet([
{id: 1, label: '1'},
{id: 2, label: '2'},
{id: 3, label: '3'}
]);
var edges = new vis.DataSet([
{id: 1, from: 1, to: 2, smooth: {type: 'curvedCW', roundness: 0.2}},
{id: 2, from: 1, to: 2, smooth: {type: 'curvedCW', roundness: -2.2}},
{id: 5, from: 1, to: 2, label: 5, smooth: {type: 'curvedCW', roundness: 0.4}},
{id: 6, from: 1, to: 2, label: 6, smooth: {type: 'curvedCW', roundness: -2.4}},
{id: 3, from: 1, to: 3, smooth: {type: 'curvedCW', roundness: -2.1}},
{id: 4, from: 1, to: 3, smooth: {type: 'curvedCW', roundness: 0.1}}
]);
var data = {
nodes: nodes,
edges: edges
};
var options = {
physics: false,
layout: {
hierarchical: {
direction: 'UD'
}
}
};
var networkContainer = document.getElementById('networkContainer');
var network = new vis.Network(networkContainer, data, options);

After disabling the physics variable, I couldn't find a useful solution for the overlap problem. for the edge. I have created a temporary solution for this.this can solve the overlap problem for a small number of edges. but for too many edges there will be overlap problem again. The roundness value sets the roundness of the edge. If we give it random values ​​in a negative and positive range, we may be out of luck. :)
getConnectedEdgesCountAndCreateRandomRoundness(toRouterId: number, fromRouterId: number): number {
var randomRoundness: number = 0;
var connectedEdgesCount = this.network.getConnectedEdges(toRouterId).filter(value => this.network.getConnectedEdges(fromRouterId).includes(value)).length;
if(connectedEdgesCount === 0){
randomRoundness = 0;
}
else if (connectedEdgesCount === 1){
randomRoundness = this.generateRandomNumber(-0.25,0.25);
}
else if (connectedEdgesCount === 2){
randomRoundness = this.generateRandomNumber(-0.5,0.5);
}
else {
randomRoundness = this.generateRandomNumber(-1.25,1.25);
}
return randomRoundness;
}
var randomRoundness = this.getConnectedEdgesCountAndCreateRandomRoundness(this.toRouterId,this.fromRouterId);
this.edges.add({
id: this.circuitEdge.id,
from: this.fromRouterNode.id,
to: this.toRouterNode.id,
label: this.circuitEdge.name,
type: this.circuitEdge.type,
color: visEdgeColor,
smooth: { enabled: true, type: "curvedCW", roundness: randomRoundness}
});

Related

Chart.js zoom instantly zooms in fully on first point no matter what type of zoom used

After adding zoom to my line chart no matter what I do it instantly zooms in to the first point without the option to zoom back out (outside of the reset zoom function). The zoom out function also doesnt work, zoom in function results in the same problem.
Normal
After any attempt to zoom
const pdmdata = JSON.parse(document.getElementById('div_pdmdata').dataset.chart);
const ctx = document.getElementById('canvasChart_1').getContext("2d");
const radars = {'hidden': 'hidden'};
let linechartData = {
datasets: [{
label: 'Constant',
data: pdmdata[0],
borderWidth: 1
}]
};
for (let i = 0; i < Object.keys(pdmdata[1]).length; i++) {
linechartData.datasets.push({
label: radars[Object.keys(pdmdata[1])[i]],
data: pdmdata[1][Object.keys(pdmdata[1])[i]],
borderWidth: 1,
tension: 0.1
});
}
const config = {
type: 'line',
data: linechartData,
options: {
maintainAspectRatio: false,
interaction: {
mode: 'index',
intersect: false,
},
stacked: false,
plugins: {
title: {
display: true,
text: 'Mode S'
},
zoom: {
zoom: {
drag: {
enabled: true,
// mode: 'x',
threshold: 24
},
mode: 'x'
}
}
},
scales: {
x: {
reverse: true
}
},
elements: {
point: {
radius: 0
}
}
},
};
myChart = new Chart(ctx, config);
function resetZoom(){
myChart.resetZoom();
}
I've tried all the zoom types; zoom, drag and pan. Even using drag in the middle of the chart makes it end up like the zoomed image above. No errors in console whatsoever. I've also tried setting the speed to 0.001 but to no avail. Weirdest thing is when I make a duplicate of this chart below it that one does zoom as intended. I have no idea why it would though.
Okay so I found my issue, chartjs-zoom does not like objects as data without also setting labels yourself. Here's an example: (Only used 1 dataset while testing to make it easier to test)
This works:
let linechartData = {
label: ['one', 'two', 'three', 'four', 'five'],
datasets: [{
label: 'Constant',
data: [1, 2, 3, 4, 5],
borderWidth: 1
}]
};
This does not work:
let linechartData = {
datasets: [{
label: 'Constant',
data: {'one': 1, 'two': 2, 'three': 3, 'four': 4, 'five': 5},
borderWidth: 1
}]
};
But this does:
let linechartData = {
label: ['one', 'two', 'three', 'four', 'five'],
datasets: [{
label: 'Constant',
data: {'one': 1, 'two': 2, 'three': 3, 'four': 4, 'five': 5},
borderWidth: 1
}]
};
So conclusion, when using chartjs-zoom always assign the labels beforehand instead of letting a dataset data object assign them automatically, which works just fine for normal charts without zoom. Never figured out why if I made a 2nd chart with the exact same config that chart did work though.

Keep selection in highcharts even on page refresh

I am using highcharts for generating/showcasing graph on UI(HTML with JavaScript Page)
There are multiple elements in series section that one can select or unselect to display on graph.
But after every refresh, user selected elements gets unselected and only the default selections appear. And user has to select the elements again.
Any solution to it?
One can relate the question from the picture:
In this, By default A is selected.
If user selects Ball or C then the point graph will appear for Ball and C as well but as soon as graph refreshes, graph will have point graph with A only.
You can store your visible series states in local storage. Example:
const savedStates = localStorage.getItem('seriesStates');
const seriesStates = savedStates ?
JSON.parse(savedStates) : {
A: true,
Ball: false,
C: false,
D: false
};
Highcharts.chart('container', {
series: [{
name: 'A',
data: [1, 2, 3],
visible: seriesStates.A
}, {
name: 'Ball',
data: [2, 2, 2],
visible: seriesStates.Ball
}, {
name: 'C',
data: [3, 3, 3],
visible: seriesStates.C
}, {
name: 'D',
data: [4, 4, 4],
visible: seriesStates.D
}],
plotOptions: {
series: {
events: {
legendItemClick: function() {
seriesStates[this.name] = !this.visible;
localStorage.setItem(
'seriesStates',
JSON.stringify(seriesStates)
);
}
}
}
}
});
Live demo: http://jsfiddle.net/BlackLabel/jkuc50Lf/
Docs: https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage

Custom edge drawing function in vis.js

I'm using the vis.js library to draw a network but I need to customize the way the edges are drawn. For example, I would like to draw let's say 50% of the edge in red and the other 50% in blue.
Is there a way to do this?
Finally found a way to do it:
var nodes = new vis.DataSet
([
{id: 1, label: '1'},
{id: 2, label: '2'},
]);
var edges = new vis.DataSet
([
{from: 1, to: 2, color:'red'},
]);
var graph = {nodes: nodes, edges: edges};
var network = new vis.Network(container, graph, options);
var percent = 50;
network.on("afterDrawing", function (ctx)
{
var pos = network.getPositions([1, 2]);
ctx.strokeStyle = ctx.filStyle = 'green';
ctx.moveTo(pos[1].x, pos[1].y);
ctx.lineTo(pos[1].x + (pos[2].x-pos[1].x)*percent/100, pos[1].y + (pos[2].y - pos[1].y)*percent/100);
ctx.fill();
ctx.stroke();
});
You cannot directly customize parts of an edge but you can create two different edges (each with its own style) using a dummy invisible node serving as a connector. Since the dummy node is invisible, the two edges are going to look like two parts of the same edge.
For example, say you want to connect two nodes with a half-green-half-red edge:
var nodes = new vis.DataSet([
{id: 1, label: '1'},
{id: 2, label: '2'},
{id: 'dummy', hidden: true}
]);
var edges = new vis.DataSet([
{from: 1, to: 'dummy', color:'red'},
{from: 'dummy', to: 2, color:'green'}
]);
var graph = {nodes: nodes, edges: edges};

vis.js - Place node manually

How do I set a node's position in vis.js?
I want to initially position at least one node manually.
I know that a node has the options x and y. I set both, and also tried variations of layout options (randomSeed, improvedLayout, hierarchical), the node was never placed where I set it.
Here's the simple network I defined:
nodes = new vis.DataSet([
{id: 1, shape: 'circularImage', image: DIR + '1_circle', label:"1", x: 200, y: 100},
{id: 2, shape: 'circularImage', image: DIR + '2_circle', label:"2"},
{id: 3, shape: 'circularImage', image: DIR + '3_circle', label:"3"},
]);
edges = [
{id: "01-03", from: 1, to: 3, length: 300, label: '1 - 3'},
{id: "02-03", from: 2, to: 3},
];
var container = document.getElementById('graphcontainer');
var data = {
nodes: nodes,
edges: edges
};
var options = {
nodes: {
borderWidth: 4,
size: 30,
color: {
border: '#222222',
background: '#666666'
},
font:{
color:'#000000'
}
},
edges: {
color: 'lightgray'
},
//layout: {randomSeed:0}
//layout: {hierarchical: true}
layout: {
randomSeed: undefined,
improvedLayout:true,
hierarchical: {
enabled:false,
levelSeparation: 150,
direction: 'UD', // UD, DU, LR, RL
sortMethod: 'hubsize' // hubsize, directed
}
}
};
network = new vis.Network(container, data, options);
The node is placed, but not at the point I set (200,100), but at another position.
I haven't found an example for explicitly setting a node's position on the vis.js page. Could someone please provide one? Thanks!
You can indeed set a fixed position for a node by setting its x and y properties, and yes, this feature works and is not broken.
The x and y position of a node does not mean a position in pixels on the screen, but is a fixed position in the Networks coordinate system. When you move and zoom in the Network, the fixed items will move and zoom too, but they will always keep the same position relative to each other. It's like your home town has a fixed location (long, lat) on earth, but you can still zoom and move your town in Google Maps.
EDIT: To achieve what you want, you can fix zooming and moving, and adjust the viewport such that it matches the pixels of the HTML canvas, here is a demo:
// create an array with nodes
var nodes = new vis.DataSet([
{id: 1, label: 'x=200, y=200', x: 200, y: 200},
{id: 2, label: 'node 2', x: 0, y: 0},
{id: 3, label: 'node 3', x: 0, y: 400},
{id: 4, label: 'node 4', x: 400, y: 400},
{id: 5, label: 'node 5', x: 400, y: 0}
]);
// create an array with edges
var edges = new vis.DataSet([
{from: 1, to: 2, label: 'to x=0, y=0'},
{from: 1, to: 3, label: 'to x=0, y=400'},
{from: 1, to: 4, label: 'to x=400, y=400'},
{from: 1, to: 5, label: 'to x=400, y=0'}
]);
// create a network
var container = document.getElementById('mynetwork');
var data = {
nodes: nodes,
edges: edges
};
var width = 400;
var height = 400;
var options = {
width: width + 'px',
height: height + 'px',
nodes: {
shape: 'dot'
},
edges: {
smooth: false
},
physics: false,
interaction: {
dragNodes: false,// do not allow dragging nodes
zoomView: false, // do not allow zooming
dragView: false // do not allow dragging
}
};
var network = new vis.Network(container, data, options);
// Set the coordinate system of Network such that it exactly
// matches the actual pixels of the HTML canvas on screen
// this must correspond with the width and height set for
// the networks container element.
network.moveTo({
position: {x: 0, y: 0},
offset: {x: -width/2, y: -height/2},
scale: 1,
})
#mynetwork {
border: 1px solid black;
background: white;
display: inline-block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vis/4.9.0/vis.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/vis/4.9.0/vis.min.css" rel="stylesheet" type="text/css" />
<p>The following network has a fixed scale and position, such that the networks viewport exactly matches the pixels of the HTML canvas.</p>
<div id="mynetwork"></div>
Documentation says that nodes positioned by the layout algorithm
When using the hierarchical layout, either the x or y position is set
by the layout engine depending on the type of view
You can put they in to explicit points but I would not recommend this - it's not the correct way for the work with the graphs - better review your task - maybe you do not need graphs (or your do not need to put points in to exactly position).
Anyway - if you really want to put in to some position then you need to use random layout with the fixed option set to true or physic option set to false
var DIR = 'http://cupofting.com/wp-content/uploads/2013/10/';
nodes = new vis.DataSet([
{id: 1, shape: 'circularImage', image: DIR + '1-circle.jpg', label:"1", x:0, y:0},
{id: 2, shape: 'circularImage', image: DIR + '2-circle.jpg', label:"2", x:100, y:0},
{id: 3, shape: 'circularImage', image: DIR + '3-circle.jpg', label:"3", x:0, y:100},
]);
edges = [
{id: "01-03", from: 1, to: 3, length: 300, label: '1 - 3'},
{id: "02-03", from: 2, to: 3},
];
var container = document.getElementById('graphcontainer');
var data = {
nodes: nodes,
edges: edges
};
var options = {
physics:{
stabilization: true,
},
nodes: {
borderWidth: 4,
size: 10,
//fixed:true,
physics:false,
color: {
border: '#222222',
background: '#666666'
},
font:{
color:'#000000'
}
},
edges: {
color: 'lightgray'
},
layout: {randomSeed:0}
};
network = new vis.Network(container, data, options);
<script src="https://cdnjs.cloudflare.com/ajax/libs/vis/4.9.0/vis.min.js"></script>
<div id="graphcontainer" style="border: 1px solid red; width:300px; height:300px; "> </div>
:) I just did this for the first time this morning. X = 0 and Y = 0 for a graph start centered, not at the top left of the canvas. There is a fixed attribute of the node that you can set to true for both x and y on a node with its x and y values set and have other nodes use physics in relation to it.
Look at the full options tab on page
http://visjs.org/docs/network/nodes.html#
and you'll see this piece:
fixed: {
x:false,
y:false
},

How to set the image size in Vis.js network graph

I am building a network graph using vis.js
The problem is, how do I make all the images the same size?
(see live example here --> comparison of wordpress & drupal)
here's the graph code:
var nodes = [];
nodes.push({
id: 7,
shape: 'image',
image: '/static/windows8_icons/PNG/Industry/circuit/circuit-26.png',
label: 'sharepoint',
widthMin: 20,
widthMax: 20
});
edges.push({
from: 1,
to: 7,
length: 100
});
var data = {
nodes: nodes,
edges: edges
};
var options = {
smoothCurves: false,
stabilize: false,
nodes: {
shape: 'image',
radius: 24,
fontSize: 18,
widthMin: 20,
widthMax: 20
},
edges: {
width: 2
}
};
network = new vis.Network(container, data, options);
Go for size attribute of node.
Example
nodes:{shape:'image',size:20}
Reference: http://visjs.org/docs/network/nodes.html

Categories

Resources