Idiomatic, purely-functional discrete event simulation.
A transition is pure stepwise function gradually modifying some arbitrary state map. Transitions are part of the state itself
and are located under the transition-key
key. They can be organized in arbitrarily nested maps. It is both common and desired
for transitions to mirror the data they act upon :
{dvlopt.dsim/transition-key {:asteroids {42 {:x ... :y ...}}} :asteroids {42 {:x 450 :y 1420}}}
This pattern is so common that in this example, [:asteroids 42 :x] would be called the data-path
of the :x transition of
asteroid 42. Such a transition accepts 3 arguments: a state map, its data-path, and a step. It returns a new state which, although
not enforced, should somehow modify the :x value of asteroid 42.
For doing so, a transition is created by providing an on-step
function which also accepts 3 arguments: a state map, the data-path,
and a percentage of completion. This percentage depends on the first step of the transition, how many steps it lasts, and the
current step: (current-step - first-step) / n-steps.
After reaching 100%, if it was provided in the first place, the on-complete
function of the transition is called. It accepts
4 arguments: the current state map, the data-path, the completion step, and the current step. It is useful when action must be
taken after a transition, for instance for creating a new one. If some steps are missed or skipped, the completion step and the
current step will not match. Hence it is useful to provide both. Completed transitions are removed automatically.
Cf. infinite
once
repeating
A poly-transition is a higher-order transition composed of several transitions. At the end of each sub-transition, the poly-transition takes care of creating the next one at the right moment. Hence, it would be easy to animate asteroid 42 to sequentially move in different directions, or to sequentially rotate in some complex manner. It is also trivial to create nested poly-transitions.
Cf. poly
poly-infinite
poly-repeating
Scaling a percentage to a value such as the :x position of an asteroid is facilitated by using scale
and fn-scale
. It is often
needed for a transition to behave non-linearly. This can be simply done by modifying the percentage of completion, which is a linear
progression, to be non-linear. For example, if an asteroid has to move faster and faster along the :x axis from 500 to 1000 pixels in a
100 steps starting from step 0:
(dvlopt.dsim/once 0 100 (let [scale' (dvlopt.dsim/fn-scale 500 1000)] (fn on-step [state data-path percent] (assoc-in state data-path (scale' (Math/pow percent 2))))))
The most straightforward way to add or remove transitions to a state is by using merge-transitions
. A series of helpers for on-step
and on-complete
functions is provided. For example, improving the last example and removing the asteroid when done :
(dvlopt.dsim/once 0 100 (dsim/fn-mirror-percent (comp (dvlopt.dsim/fn-scale 500 1000) #(Math/pow % 2))) dsim/remove-pre-data)
The most basic way of moving a state to some step is done by using move
. move-seq
facilitates the process of iteratively moving
through a sequence of steps. However, the most useful way is probably move-events
which also takes into account events happening at
some particular steps, each modifying the state is some way. Any non-trivial simulation involves such events.
Idiomatic, purely-functional discrete event simulation. A transition is pure stepwise function gradually modifying some arbitrary state map. Transitions are part of the state itself and are located under the `transition-key` key. They can be organized in arbitrarily nested maps. It is both common and desired for transitions to mirror the data they act upon : {dvlopt.dsim/transition-key {:asteroids {42 {:x ... :y ...}}} :asteroids {42 {:x 450 :y 1420}}} This pattern is so common that in this example, [:asteroids 42 :x] would be called the `data-path` of the :x transition of asteroid 42. Such a transition accepts 3 arguments: a state map, its data-path, and a step. It returns a new state which, although not enforced, should somehow modify the :x value of asteroid 42. For doing so, a transition is created by providing an `on-step` function which also accepts 3 arguments: a state map, the data-path, and a percentage of completion. This percentage depends on the first step of the transition, how many steps it lasts, and the current step: (current-step - first-step) / n-steps. After reaching 100%, if it was provided in the first place, the `on-complete` function of the transition is called. It accepts 4 arguments: the current state map, the data-path, the completion step, and the current step. It is useful when action must be taken after a transition, for instance for creating a new one. If some steps are missed or skipped, the completion step and the current step will not match. Hence it is useful to provide both. Completed transitions are removed automatically. Cf. `infinite` `once` `repeating` A poly-transition is a higher-order transition composed of several transitions. At the end of each sub-transition, the poly-transition takes care of creating the next one at the right moment. Hence, it would be easy to animate asteroid 42 to sequentially move in different directions, or to sequentially rotate in some complex manner. It is also trivial to create nested poly-transitions. Cf. `poly` `poly-infinite` `poly-repeating` Scaling a percentage to a value such as the :x position of an asteroid is facilitated by using `scale` and `fn-scale`. It is often needed for a transition to behave non-linearly. This can be simply done by modifying the percentage of completion, which is a linear progression, to be non-linear. For example, if an asteroid has to move faster and faster along the :x axis from 500 to 1000 pixels in a 100 steps starting from step 0: (dvlopt.dsim/once 0 100 (let [scale' (dvlopt.dsim/fn-scale 500 1000)] (fn on-step [state data-path percent] (assoc-in state data-path (scale' (Math/pow percent 2)))))) The most straightforward way to add or remove transitions to a state is by using `merge-transitions`. A series of helpers for `on-step` and `on-complete` functions is provided. For example, improving the last example and removing the asteroid when done : (dvlopt.dsim/once 0 100 (dsim/fn-mirror-percent (comp (dvlopt.dsim/fn-scale 500 1000) #(Math/pow % 2))) dsim/remove-pre-data) The most basic way of moving a state to some step is done by using `move`. `move-seq` facilitates the process of iteratively moving through a sequence of steps. However, the most useful way is probably `move-events` which also takes into account events happening at some particular steps, each modifying the state is some way. Any non-trivial simulation involves such events.
cljdoc is a website building & hosting documentation for Clojure/Script libraries
× close