normaliztation is now imlied

This commit is contained in:
JuliusHerrmann 2021-10-25 22:02:00 +02:00
parent 40b1a9688b
commit 5016e00fb6
6 changed files with 131 additions and 42 deletions

View File

@ -1,29 +1,39 @@
import React from 'react'; import React from 'react';
import CytoscapeComponent from 'react-cytoscapejs'; import CytoscapeComponent from 'react-cytoscapejs';
import '../css/Graph.css' import '../css/Graph.css'
import Slider from './Slider';
class GraphCytoscape extends React.Component { class GraphCytoscape extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.animationLength = 101;
this.cy = React.createRef(); this.cy = React.createRef();
this.iteration = 0; this.stepTime = 0;
this.state ={animationDuration: 4}; this.neverPlayed = true;
this.state ={animationDuration: 4, step: 0, playing: false};
} }
componentDidMount() { componentDidMount() {
//initial layout //initial layout
this.layoutGraph(); this.layoutGraph();
this.iteration = 0; this.setState({step: 0}, () => {
this.visualizeOneStep(); //crop animation
this.cropAnimation();
this.visualizeOneStep(false);
});
} }
componentDidUpdate(prevProps, prevState) { componentDidUpdate(prevProps, _) {
//only recalculate the layout if graph has changed //only recalculate the layout if graph has changed
if (prevProps.graphData !== this.props.graphData) { if (prevProps.graphData !== this.props.graphData) {
this.layoutGraph(); this.layoutGraph();
this.iteration = 0; this.setState({step: 0}, () => {
//first crop the animation
this.cropAnimation();
clearInterval(this.animationId); clearInterval(this.animationId);
this.visualizeOneStep(); this.setState({playing: false});
this.visualizeOneStep(false);
});
} }
//we could check here if the simulation should start displaying //we could check here if the simulation should start displaying
@ -53,7 +63,7 @@ class GraphCytoscape extends React.Component {
// The layout animates only after this many milliseconds for animate:true // The layout animates only after this many milliseconds for animate:true
// (prevents flashing on fast runs) // (prevents flashing on fast runs)
animationThreshold: 250, animationThreshold: 250,
// Number of iterations between consecutive screen positions update // Number of steps between consecutive screen positions update
refresh: 20, refresh: 20,
// Whether to fit the network view after when done // Whether to fit the network view after when done
fit: true, fit: true,
@ -79,11 +89,11 @@ class GraphCytoscape extends React.Component {
nestingFactor: 1.2, nestingFactor: 1.2,
// Gravity force (constant) // Gravity force (constant)
gravity: 1, gravity: 1,
// Maximum number of iterations to perform // Maximum number of steps to perform
numIter: 1000, numIter: 1000,
// Initial temperature (maximum node displacement) // Initial temperature (maximum node displacement)
initialTemp: 1000, initialTemp: 1000,
// Cooling factor (how the temperature is reduced between consecutive iterations // Cooling factor (how the temperature is reduced between consecutive steps
coolingFactor: 0.99, coolingFactor: 0.99,
// Lower temperature threshold (below this point the layout will end) // Lower temperature threshold (below this point the layout will end)
minTemp: 1.0 minTemp: 1.0
@ -97,36 +107,77 @@ class GraphCytoscape extends React.Component {
this.setState({animationDuration: e.target.value}); this.setState({animationDuration: e.target.value});
} }
//remove steps where the animation does not change
cropAnimation = () => {
console.log("hello");
var data = this.props.simulationData;
var lastState = data.length - 1;
for (var i = data.length - 1; i > 0; i--) {
//if (this.checkIfStatesAreEqual(data, lastState, i)) {
//lastState = i;
//}
if (data[lastState] === data[i]) {
lastState = i;
}
}
this.animationLength = lastState + 1;
}
checkIfStatesAreEqual(data, stateOne, stateTwo) {
for (var i = 0; i < stateOne.length; i++) {
if (stateOne[i] !== stateTwo[i]) {
return false;
}
}
return true;
}
visualizeSimulation = () => { visualizeSimulation = () => {
//check if we pause the animation if (this.state.playing) {
console.log("new animation started");
clearInterval(this.animationId); clearInterval(this.animationId);
this.iteration = 0; this.setState({playing: false});
if (this.state.animationDuration <= 0) { return;
this.setState({animationDuration: 4}); } else if (this.state.step < this.animationLength && !this.neverPlayed) {
this.animationId = setInterval(this.visualizeOneStep, this.stepTime);
this.setState({playing: true});
return;
} }
this.visualizeOneStep(); this.neverPlayed = false;
var stepTime = this.state.animationDuration * 1000 / this.props.simulationData.length; //check if we pause the animation
this.animationId = setInterval(this.visualizeOneStep, stepTime); clearInterval(this.animationId);
console.log("new animation started");
this.setState({playing: false});
//first we need to normalize the distribution
this.props.normalize();
this.setState({step: 0}, () => {
if (this.state.animationDuration <= 0) {
this.setState({animationDuration: Math.abs(this.state.animationDuration)});
}
this.stepTime = (this.state.animationDuration) * 1000 / this.animationLength;
this.animationId = setInterval(this.visualizeOneStep, this.stepTime);
this.setState({playing: true});
//update the button //update the button
this.setState({}); this.setState({});
});
} }
//this is the method to visualize the simulation //this is the method to visualize the simulation
visualizeOneStep = () => { visualizeOneStep = (increment = true) => {
//the data of the simulation is stored in: this.props.simulationData //the data of the simulation is stored in: this.props.simulationData
var data = this.props.simulationData; var data = this.props.simulationData;
if (data == null) { if (data == null) {
return; return;
} }
var simulationLength = data.length; if (this.state.step >= this.animationLength) {
if (this.iteration >= simulationLength) {
console.log("finished animation"); console.log("finished animation");
clearInterval(this.animationId); clearInterval(this.animationId);
this.iteration = 0; this.setState({playing: false});
this.setState({});
return; return;
//this.setState({step: 0}, () => {
//return;
//});
} }
//if this is null do not continue => bug //if this is null do not continue => bug
@ -139,23 +190,58 @@ class GraphCytoscape extends React.Component {
//do one step //do one step
for (var i = 0; i < allNodes.length; i++) { for (var i = 0; i < allNodes.length; i++) {
//the current state of the current node //the current state of the current node
let state = data[this.iteration][i]; let state = data[this.state.step][i];
var color = this.props.colors.find(element => element[0] === state)[1]; var color = this.props.colors.find(element => element[0] === state)[1];
this.cy.getElementById(allNodes[i].id()).style('background-color', color); this.cy.getElementById(allNodes[i].id()).style('background-color', color); }
if (increment) {
this.setState({step: this.state.step + 1});
} else {
//update
this.setState({});
}
} }
this.iteration++; visualizeSpecificStep = (e) => {
var val = Number(e.target.value);
if (val < 0 && val > this.animationLength) {
return;
}
//first stop the current animation
clearInterval(this.animationId);
this.setState({playing: false});
this.setState({step: val}, () => {
this.visualizeOneStep(false);
});
}
recalculate = () => {
//normalize
this.props.normalize();
//then recalculate
this.props.recalculateFuntion();
}
renderPlayPauseButton = () => {
if (this.state.playing) {
return
}
} }
render() { render() {
var playPauseString = "Play Simulation ▶️";
if (this.state.playing) {
playPauseString = "Stop Simulation ⏹️";
}
return (<div id="graphDiv"> return (<div id="graphDiv">
<button id="recalculate" onClick={this.props.recalculateFuntion}>Recalculate 🔁</button> <button id="recalculate" onClick={this.recalculate}>Recalculate 🔁</button>
<button id="runSimulationButton" onClick={this.visualizeSimulation}>Play Simulation </button> <button id="runSimulationButton" onClick={this.visualizeSimulation}>{playPauseString}</button>
<div> <div>
<h3 id="durationDescription">Duration (seconds): </h3> <h3 id="durationDescription">Duration (seconds): </h3>
<input id="animationDuration" type="number" onChange={this.changeAnimationDuration} value={this.state.animationDuration}/> <input id="animationDuration" type="number" onChange={this.changeAnimationDuration} value={this.state.animationDuration}/>
</div> </div>
<Slider description="Step" min="0" max={this.animationLength} currentValue={this.state.step} handleChange={this.visualizeSpecificStep}/>
<CytoscapeComponent id="cy" userZoomingEnabled={false} userPanningEnabled={false} <CytoscapeComponent id="cy" userZoomingEnabled={false} userPanningEnabled={false}
cy={(cy) => { this.cy = cy }} elements={this.props.graphData}/> cy={(cy) => { this.cy = cy }} elements={this.props.graphData}/>
</div>); </div>);

View File

@ -52,7 +52,7 @@ class ModelSelector extends React.Component{
return( return(
<div id='ModelSelector'> <div id='ModelSelector'>
<h2 id='ModelSelectorName' className='selectorName'> <h2 id='ModelSelectorName' className='selectorName'>
Contact Model</h2> Spreading Model</h2>
<Dropdown name='Select Model' <Dropdown name='Select Model'
description='Select a network' description='Select a network'
options={this.state.predefinedModels} options={this.state.predefinedModels}

View File

@ -91,7 +91,7 @@ class Simulation extends React.Component{
</div> </div>
<div id="SimulationGraph"> <div id="SimulationGraph">
<GraphCytoscape recalculateFuntion={this.recalculate} <GraphCytoscape recalculateFuntion={this.recalculate}
graphData={this.state.graphData} simulationData={this.state.simulationData} colors={this.state.selectedModel.getColors()}/> graphData={this.state.graphData} simulationData={this.state.simulationData} colors={this.state.selectedModel.getColors()} normalize={this.state.selectedModel.normalizeDistribution}/>
</div> </div>
</div> </div>
); );

View File

@ -106,7 +106,7 @@ class Model extends React.Component {
this.states[spot][1] = Number(e.target.value); this.states[spot][1] = Number(e.target.value);
//update //update
this.props.updateSelectedModel(this); this.props.updateSelectedModel(this);
this.checkValidity(); //this.checkValidity();
} }
changeColor = (spot, e) => { changeColor = (spot, e) => {
@ -137,7 +137,8 @@ class Model extends React.Component {
//build the sliders for the initial distribution //build the sliders for the initial distribution
buildSlidersDistribution() { buildSlidersDistribution() {
var output = [<button key="normalization" id="normalizeDistribution" onClick={this.normalizeDistribution}>Normalize distribution 🔔</button>, <DistributionStatus key="validation" valid={this.valid}/>]; //var output = [<button key="normalization" id="normalizeDistribution" onClick={this.normalizeDistribution}>Normalize distribution 🔔</button>, <DistributionStatus key="validation" valid={this.valid}/>];
var output = [];
var count = 0; var count = 0;
this.states.forEach((s) => { this.states.forEach((s) => {
//clamp description //clamp description
@ -160,9 +161,9 @@ class Model extends React.Component {
render() { render() {
return ( return (
<div className="modelSelector"> <div className="modelSelector">
<h3 id="selectRulesHeader">Change state transition probabilities</h3> <h3 id="selectRulesHeader">Transition Rates</h3>
{this.buildSliders(this.ruleMax, this.ruleStep)} {this.buildSliders(this.ruleMax, this.ruleStep)}
<h3 id="selectDistributionHeader">Change initial distribution of states</h3> <h3 id="selectDistributionHeader">Initial Distribution</h3>
{this.buildSlidersDistribution()} {this.buildSlidersDistribution()}
</div> </div>
); );

View File

@ -19,6 +19,8 @@
background-color: #0A8E4E; background-color: #0A8E4E;
border-radius: 10px; border-radius: 10px;
padding: 5px 10px; padding: 5px 10px;
text-align: left;
min-width: 295px;
} }
#durationDescription { #durationDescription {