added basic controls

This commit is contained in:
JuliusHerrmann 2021-06-16 23:19:52 +02:00
parent 0b7f3f2be5
commit a9b70d61a8
6 changed files with 166 additions and 55 deletions

6
:w Normal file
View File

@ -0,0 +1,6 @@
#mynetwork {
height: 500px;
width: 500px;
background-color: white;
border: 4px black;
}

View File

@ -1,5 +1,6 @@
#mynetwork {
height: 500px;
width: 500px;
background-color: black;
background-color: white;
border: 3px solid black;
}

View File

@ -1,34 +1,13 @@
// create an array with nodes
//var nodes = new vis.DataSet([
//{ id: 1, label: "Node 1" },
//{ id: 2, label: "Node 2" },
//{ id: 3, label: "Node 3" },
//{ id: 4, label: "Node 4" },
//{ id: 5, label: "Node 5" }
//]);
//
//// create an array with edges
//var edges = new vis.DataSet([
//{ from: 1, to: 3 },
//{ from: 1, to: 2 },
//{ from: 2, to: 4 },
//{ from: 2, to: 5 },
//{ from: 3, to: 3 }
//]);
// create a network
//var container = document.getElementById("mynetwork");
//var data = {
//nodes: nodes,
//edges: edges
//};
//var options = {
//height: '100%',
//width: '100%',
//};
//var network = new vis.Network(container, data, options);
var network;
var simulationData;
animationPlaying = false;
currentAnimationStep = 0;
var animationInterval;
var animationLen = 10;
const stepSlider = document.querySelector('#stepRange');
const stepField = document.querySelector('#stepField');
const animationDurationSlider = document.querySelector('#animationDurationRange');
const animationDurationField = document.querySelector('#animationDurationField');
function createGraphFromDot(dotString){
var parsedData = vis.parseDOTNetwork(dotString);
// create a network
@ -42,6 +21,10 @@ function createGraphFromDot(dotString){
width: '100%',
autoResize: true,
nodes:{
shape: 'dot',
size: 40,
borderWidth: 0,
chosen: false,
font:{
size:0
},
@ -50,18 +33,18 @@ function createGraphFromDot(dotString){
randomSeed: undefined,
improvedLayout:true,
clusterThreshold: 150,
//hierarchical: {
//enabled:false,
//levelSeparation: 150,
//nodeSpacing: 100,
//treeSpacing: 200,
//blockShifting: true,
//edgeMinimization: true,
//parentCentralization: true,
//direction: 'UD', // UD, DU, LR, RL
//sortMethod: 'hubsize', // hubsize, directed
//shakeTowards: 'leaves' // roots, leaves
//}
hierarchical: {
enabled:false,
levelSeparation: 150,
nodeSpacing: 100,
treeSpacing: 200,
blockShifting: true,
edgeMinimization: true,
parentCentralization: true,
direction: 'UD', // UD, DU, LR, RL
sortMethod: 'hubsize', // hubsize, directed
shakeTowards: 'leaves' // roots, leaves
}
},
};
network = new vis.Network(container, data, options);
@ -74,14 +57,6 @@ function createGraphFromDot(dotString){
//}
//});
//});
//console.log(network.selectNodes([0]));
n = network.body.nodes[0];
n.setOptions({
color:{
background: "#ffffff",
}
})
}
function visualizeOneStep(step){
@ -102,6 +77,7 @@ function visualizeOneStep(step){
});
}
}
network.redraw();
}
@ -110,7 +86,9 @@ function getSimulation(){
.then(response => response.json())
.then(response =>{
simulationData = response;
stepSlider.max = simulationData.steps - 1;
createGraphFromDot(simulationData.dotGraph);
visualizeOneStep(simulationData.states[0]);
//kelvin to celsius
//let temp = Math.ceil(response.main.temp - 273.15);
//document.getElementById("weather").innerHTML = temp + "ºC";
@ -118,4 +96,56 @@ function getSimulation(){
});
}
function playAnimation(){
if(!animationPlaying){
currentAnimationStep = 0;
//Play the animation
animationInterval = setInterval(function(){
//This is the animation routine
if(currentAnimationStep == simulationData.steps){
//At the end of animation
clearInterval(animationInterval);
animationPlaying = false;
return;
}
visualizeOneStep(simulationData.states[currentAnimationStep]);
currentAnimationStep ++;
stepSlider.value = currentAnimationStep;
stepField.value = currentAnimationStep;
}, animationLen * 1000 / simulationData.steps);
}else{
//stop the animation
clearInterval(animationInterval);
currentAnimationStep = 0;
}
//switch boolean
animationPlaying = !animationPlaying;
}
stepField.value = 0;
stepSlider.min = 0;
stepSlider.max = 0;
stepSlider.addEventListener('input', function(){
stepField.value = stepSlider.value;
visualizeOneStep(simulationData.states[stepSlider.value]);
//console.log(stepSlider.value);
});
stepField.addEventListener('input', function(){
stepSlider.value = stepField.value;
visualizeOneStep(simulationData.states[stepField.value]);
//console.log(stepSlider.value);
});
animationDurationField.value = 10;
animationDurationSlider.value = 10;
animationDurationSlider.addEventListener('input', function(){
animationLen = animationDurationSlider.value;
animationDurationField.value = animationLen;
});
animationDurationField.addEventListener('input', function(){
animationLen = animationDurationField.value;
animationDurationSlider.value = animationLen;
});
getSimulation()

View File

@ -34,12 +34,13 @@ def gen_sis(G, steps = 1000, inf_rate=1.0, rec_rate=2.0, noise=0.1, statesList =
jump_time = np.random.exponential(rates)
change_n = np.argmin(jump_time)
states[change_n] = S if states[change_n] == I else I
statesList.append(states)
statesList.append(states.copy())
return states
statesList = []
steps = 1000 + random.choice(range(1000))
G_grid10x10 = nx.grid_2d_graph(10,10)
# G_grid10x10 = nx.grid_2d_graph(10,10)
G_grid10x10 = nx.newman_watts_strogatz_graph(120, 4, 0.15, seed=42)
G = clean_shuffle_graph(G_grid10x10)
TS_data = gen_sis(G, steps=steps,statesList=statesList)
# print(G.number_of_nodes())

View File

@ -12,7 +12,12 @@
<script src="{{ asset('js/main.js') }}"></script>
{% endblock %}
{% block body %}
<h1>Hello {{ controller_name }}! ✅</h1>
<div id="mynetwork"></div>
<label for="step">Choose a step</label>
<input type="range" id="stepRange" name="step">
<input type="number" id="stepField" for="step" min=0></output>
<label for="animationDuration">Choose animation duration</label>
<input type="range" id="animationDurationRange" name="animationDuration" min=10 max=30>
<input type="number" id="animationDurationField" for="animationDuration" min=10 max=30></output>
<button id="playAnimation" onclick="playAnimation()">Run</button>
<div id="mynetwork"></div>
{% endblock %}

68
ä Normal file
View File

@ -0,0 +1,68 @@
import math, random, os, time, sys, io
import json
import numpy as np
import networkx as nx
import scipy
import time
from networkx.drawing.nx_pydot import write_dot
def clean_shuffle_graph(G):
random_seed_state = int(random.random()*100000) # quick hack to go back to random afterwards
random.seed(42)
node_mapping = dict(zip(sorted(G.nodes()), sorted(G.nodes(), key=lambda _: random.random()))) # maybe sorted not really deterministic
G = nx.relabel_nodes(G, node_mapping)
G = nx.convert_node_labels_to_integers(G)
if not nx.is_connected(G):
print('Graph is not connected, try a differnt one.')
assert(nx.is_connected(G))
random.seed(random_seed_state)
return G
def gen_sis(G, steps = 1000, inf_rate=1.0, rec_rate=2.0, noise=0.1, statesList = None):
S = [1., 0.]
I = [0., 1.]
states = [random.choice([S, I]) for i in range(G.number_of_nodes())]
for _ in range(steps):
rates = np.zeros(G.number_of_nodes())
for n in range(G.number_of_nodes()):
rates[n] = noise
if states[n] == I:
rates[n] += rec_rate
if states[n] == S:
rates[n] += inf_rate * len([n_j for n_j in G.neighbors(n) if states[n_j] == I])
rates[n] = 1.0/rates[n] # numpy uses mean as rate param
jump_time = np.random.exponential(rates)
change_n = np.argmin(jump_time)
states[change_n] = S if states[change_n] == I else I
statesList.append(states.copy())
return states
statesList = []
steps = 1000 + random.choice(range(1000))
G_grid10x10 = nx.grid_2d_graph(10,10)
G = clean_shuffle_graph(G_grid10x10)
TS_data = gen_sis(G, steps=steps,statesList=statesList)
# print(G.number_of_nodes())
# f = open("graph.dot", "rw")
# nx.drawing.nx_pydot.write_dot(G,f)
# f.write("\n")
# f.close()
# Writing the json file to stdout
# f = StringIO("")
dotGraph = ""
#Write dot file to string
with io.StringIO() as f:
write_dot(G, f)
f.seek(0)
dotGraph = f.read()
output = {
"name" : "testGraph",
"dotGraph" : dotGraph,
"steps" : steps,
"states" : statesList,
}
jsonOutput = json.dumps(output)
print(jsonOutput)