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.
Related
I am using the pdfMake and I Ihave a text next to a image I would like to repeat that part in my docdefintion.
I would like to repeate this part of my pdfmake
{
columns: [
{
image: "checked",
height: 10,
width: 10
},
{
stack: [
{
columns: [
{
text: 'First column first line',
width: '50%',
margin: [5, 0, 0, 0],
}
]
}
],
width: '*'
}]
}
Here is my docDefinition
let docDefinition = {
pageSize: 'LEGAL',
//pageOrientation: 'landscape', //may be usefull in some case
pageMargins: [40, 80, 40, 60],
content: [
{
...
this.getFailureLocationObject(),
...
}
};
pdfMake.createPdf(docDefinition).download('Intervention' + '19060023');
}
I have made this function that should return a object list (ol) I have o the object that I want to push into ol when the function retuns o there is no problem my image is displayed next to the text However when I return ol insted there is a wierd result where the image and text are no longer aligned and no mater how may objects I add to ol the result is the same there is only one image displayed next to the text. How can I fix this issues thank you for your help.
getFailureLocationObject() {
const ol = [];
var o = {
columns: [
{
image: "checked",
height: 10,
width: 10
},
{
stack: [
{
columns: [
{
text: 'First column first line',
width: '50%',
margin: [5, 0, 0, 0],
}
]
}
],
width: '*'
}]
};
ol.push(o);
ol.push(o);
return o;
}
Here you can try what I have made so far. And see the issus I have hard coded 'First column first line','First column Second line','First column Third line'. However I would like the method this.getFailureLocationObject(), too loop and make the list.
Try it out here!
https://stackblitz.com/edit/angular-pdfmake-example-3f14km?file=src%2Fapp%2Fapp.component.ts
After a bit of testing with the code you provided, I figured out that the problem is the reference of the objects.
So, when pdfmake gets the values, somehow, if both have same reference (are the same object) it mixes them.
I came to this conclusion because when you change the code you have with:
getFailureLocationObject() {
const ol = [];
var o = {
columns: [
{
image: 'checked',
height: 10,
width: 10,
},
{
stack: [
{
columns: [
{
text: 'First column first line',
width: '50%',
margin: [5, 0, 0, 0],
},
],
},
],
width: '*',
},
],
};
ol.push(o);
o = JSON.parse(JSON.stringify(o));
ol.push(o);
return ol;
}
The error no longer occurs. Note that JSON.parse(JSON.stringify(o)) creates a new JSON object with the same content as o, meaning the two objects in the array are different (as the objects are not the same)
This might be an issue to be reported to the developers of the plugin to be fixed. For now, the solution is to add different objects to the array using clone. There is a good explaination on how and why to use cloning here.
Feel free to ask for clarification if needed!
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);
my real objective is to associate graphically two lists together.
I want to do it in interactively in jupyter/jupyterlab.
An example can be find in this library :
http://sebastien.drouyer.com/jquery.flowchart-demo/
My problem is that I'm not at ease with integrating Javascript into ipython notebook.
I found some tutorials : http://jacobbridges.github.io/post/jupyter-notebook-with-interactive-charts/
If I'm able to reproduce this example, I'm not try to replace by flowchart library.
First Cell
%%javascript
require.config({
paths: {
flowcharts: "https://github.com/sdrdis/jquery.flowchart/blob/master/jquery.flowchart.js",
},
shim: {
flowcharts: {
exports: "FlowCharts",
deps: ["jquery"]
},
}
});
Second
%%javascript
// Since I append the div later, sometimes there are multiple divs.
$("#example_1").remove();
// Make the cdiv to contain the chart.
element.append('<div id="example_1" style="min-width: 310px; height: 400px; margin: 0 auto"></div>');
var data = {
operators: {
operator: {
top: 20,
left: 20,
properties: {
title: 'Operator',
inputs: {
input_1: {
label: 'Input 1',
},
input_2: {
label: 'Input 2',
}
},
outputs: {
output_1: {
label: 'Output 1',
},
output_2: {
label: 'Output 2',
},
output_3: {
label: 'Output 3',
}
}
}
}
}
};
// Require highcarts and make the chart.
require(['flowcharts'], function(FlowCharts) {
$('#example_1').flowchart({
data: data
});
});
I also don't know where to import css file.
Can someone help me with this ?
Regards,
Thomas
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.
I have a fairly simple hierarchical structure of nodes, but when vis.js draws them, the order of nodes on each level doesn't make much sense - there are a lot of crossed edges (screenshot: Default Layout )
I am hoping to get a layout similar to that given here:
Expected Layout
My vis.js options are as follows;
{
"layout": {
"hierarchical": {
"direction": "LR",
"sortMethod": "directed",
"nodeSpacing": 200,
"treeSpacing": 400
}
},
"edges": {
"smooth": {
"type": "continuous"
}
},
"nodes": {
"physics": false
}
};
What is the best method to produce this sorted layout?
I suggest your try enabling physics, which will sort out the edges crossing, etc.
However, in hierarchical layout, it's a good idea to disable the engine once it's done the first iterations by catching the 'stabilizationIterationsDone' event as follows:
network.on("stabilizationIterationsDone", function(){
network.setOptions( { physics: false } );
});
you should remove the quotation marks. these are object's properties, not strings. it should look like this:
layout: {
hierarchical: {
direction: "LR",
sortMethod: "directed",
nodeSpacing: 200,
treeSpacing: 400
}
},
edges: {
smooth: {
type: "continuous"
}
},
nodes: {
physics: false
}