How Time Works
Time is one of the central notions of react-sim.
A react-sim Model will maintain:
tick(integer), a representation of the progress in the simulation,data(anything), the internal state of the data to be shown,isPlaying(boolean), a flag that controls whether the animation is running or not.
In addition to these main properties, Model also maintains:
cachedData(object), which caches data which has already been calculated for a giventick,results(array), where information on the previous runs of the simulation can be stored.
The simulation author is expected to pass properties such as:
initialParams(object), a list of params for the simulation. They can be overridden by the controls.initData(function), which takesparamsas input and outputs the first value fordata.updateData(function), which takes:params,data,tick,cachedData, andresultsas inputs, and outputs a new value fordata.
The simulation lifecycle
Initial state
When a <Model /> is created it will:
- initiate the interal
paramsfrom the values ofinitParams. - initiate
tick- either through theminTimeor theinitialTickprop.minTimetakes precedence. - run
initDataand generate a first value fordatafrom theparams.
The frames will not render until initData has run, but they will render once data has been initialized once.
If the isPlaying prop of the <Model /> is set to true, it will start playing. Else, the simulation doesn't change until controls trigger it.
Playing
When the simulation is playing, every so often it will try to update data and refresh the Frames.
By default, the simulation refreshes 60 times per second.
It's possible to make it go slower by providing a delay prop to Model. The simulation will wait that long (in ms) to refresh the animation.
Each time the animation refreshes, the simulation will progress by 1 tick (i.e. the tick value will increase by 1.)
It's also possible to make it go faster with a ticksPerAnimation prop. If this value is greater than 1, then the simualtion will try to update data that many times before re-rendering Frames.
We can use both values at once. For instance, we can have a simulation that, every 100ms, updates 500 times.
Playing stops if:
- the user pauses or stops the simulation,
ticksreaches the maximum value,- the simulation completes.
When the simulation pauses, isPlaying simply switches to false. Nothing else changes, tick stays the same, so does data, etc.
The default timer provides an easy way for a user to trigger a pause. Model has an internal method that pauses, which can be exposed to controls.
When the simulation stops, isPlaying switches to false, but data is also re-initiated:
tickgoes back to its original value,initDatafires again and overwritesdata,cachedDatais emptied,resultsis left unchanged.
The simulation completes when updateData triggered a condition where it can't go any further.
When this happens:
isPlayingswitches to false, but:tickis unchanged,datais unchanged,- A
resultfor this run may be appended toresults.
The simulation won't reset unless manually triggered.
When to update or cache data
Each time tick changes, data is updated.
If using cached data, and tick changes to a value for which cachedData has precomputed values, then the simulation will simply retrieve those values and not do further calculations.
This can happen, for instance, if a user uses the time slider to move back in time.
Else, we're going to figure out which is the latest value of tick for which we have data, and through how many cycles do we have to go to get to the current value of tick - which can be just 1 if tick is simply incrementing along the animation, but which could be more if ticksPerAnimation is set, or if we are moving foward in time with the time slider, and run updateData as many times as needed.
updateData will stop running if:
tickreaches the maximum value, or- the simulation completes.
Each time it successfully runs, if caching data, it will update cachedData.
If a simulation is never going to complete, and has no maximum time value, and there's no reason to go back in time, then it may be a good idea to disable caching, as the system will eventually run out of memory. You can do that by setting the noCache property of the Model to true.
Updating data
Updating data is done through the updateData function, which takes as arguments an object with the following properties:
data: the existingdata,tick: the next value oftick,params: all theparamsof the simulation,cachedData: the cached values ofdatafor previous ticks,results: the results of previous runs,complete: a function that signals that the simulation is complete. If it's provided an argument (result), that is added to theresultsproperty of the simulation.
updateData will return an updated value for data. Even if it completes, it's expected to return data.
The main idea behind updateData taking the existing data as an argument is that it can operates as a mathematical sequence.
For example, in the following Fibonacci spiral example:
the updateData function is a very simple recursion:
function updateData({ data, tick }) {if (tick === 0) {return [0];}if (tick === 1) {return [0, 1];}const lastNumber = data[tick - 1] + data[tick - 2];return [...data, lastNumber];}
But updateData has access to other properties of the simulation, and can use past values of data, or even information on previous runs of the simulation, as needed.
updateData can also not use any of these arguments and provide values unrelated to the previous state of the simulation.