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.
(deep-merge hmap-1 hmap-2)
Deep merges two maps.
Deep merges two maps.
(dissoc-in hmap [k & ks :as path])
Deep dissoc, natural counterpart of Clojure's assoc-in
.
Empty maps are removed.
Ex. (dissoc-in {:a {:b 42} :c :ok} [:a :b])
=> {:c :ok}
Deep dissoc, natural counterpart of Clojure's `assoc-in`. Empty maps are removed. Ex. (dissoc-in {:a {:b 42} :c :ok} [:a :b]) => {:c :ok}
(fn-assoc-data data)
Returns a function assoc'ing the given data at the data-path of a transition.
Useful when some steps might be skipped but it is needed for a transition to reach 100%. For instance,
during a live animation, a frame will probably not be drawn at the exact millisecond a transition should
complete but some milliseconds later. The returned function can be used as an on-complete
function
so that the state will always reflect the last step of such a transition.
Returns a function assoc'ing the given data at the data-path of a transition. Useful when some steps might be skipped but it is needed for a transition to reach 100%. For instance, during a live animation, a frame will probably not be drawn at the exact millisecond a transition should complete but some milliseconds later. The returned function can be used as an `on-complete` function so that the state will always reflect the last step of such a transition.
(fn-infinite n-steps on-step)
Returns a function returning an infinite transition.
Useful for poly-transitions.
Cf. infinite
poly
Returns a function returning an infinite transition. Useful for poly-transitions. Cf. `infinite` `poly`
(fn-mirror map-percent)
Given an on-step
function returning some arbitrary value instead of a new state, returns an on-step
function assoc'ing this value at the data-path in the state.
Idiomatic.
Given an `on-step` function returning some arbitrary value instead of a new state, returns an `on-step` function assoc'ing this value at the data-path in the state. Idiomatic.
(fn-mirror-percent map-only-percent)
Behaves just like fn-mirror
but the function provided in the first place simply maps a percent value to
an arbitrary one instead of being an on-step
function.
Small convenient helper when the current state and data-path are not needed.
Behaves just like `fn-mirror` but the function provided in the first place simply maps a percent value to an arbitrary one instead of being an `on-step` function. Small convenient helper when the current state and data-path are not needed.
(fn-once n-steps on-step)
(fn-once n-steps on-step on-complete)
Returns a function returning a transition.
Useful for poly-transitions.
Cf. once
poly
Returns a function returning a transition. Useful for poly-transitions. Cf. `once` `poly`
(fn-poly fn-transitions)
(fn-poly fn-transitions on-complete)
Returns a function returning a poly-transition.
Useful for nested poly-transitions.
Cf. poly
Returns a function returning a poly-transition. Useful for nested poly-transitions. Cf. `poly`
(fn-poly-infinite fn-transitions)
Returns a function returning an infinite poly-transition.
Useful for nested poly-transitions.
Cf. poly
poly-infinite
Returns a function returning an infinite poly-transition. Useful for nested poly-transitions. Cf. `poly` `poly-infinite`
(fn-poly-repeating n-times fn-transitions)
(fn-poly-repeating n-times fn-transitions on-complete)
Returns a function returning a repeating poly-transition.
Useful for nested poly-transitions.
Cf. poly
poly-repeating
Returns a function returning a repeating poly-transition. Useful for nested poly-transitions. Cf. `poly` `poly-repeating`
(fn-repeating n-times n-steps on-step)
(fn-repeating n-times n-steps on-step on-complete)
Returns a function returning a repeating transition.
Useful for poly-transitions.
Cf. repeating
poly
Returns a function returning a repeating transition. Useful for poly-transitions. Cf. `repeating` `poly`
(fn-scale scaled-a scaled-b)
(fn-scale scaled-a scaled-b a b)
Exactly like scale
but does not accept a value to scale. Instead, returns a function which does so.
Particularly useful when working with the percentage of completion of transitions.
Exactly like `scale` but does not accept a value to scale. Instead, returns a function which does so. Particularly useful when working with the percentage of completion of transitions.
(in-transition? state)
(in-transition? state data-path)
Is the given state or some part of it currently in transition?
Is the given state or some part of it currently in transition?
(infinite first-step n-steps on-step)
Returns a transition endlessly repeating cycles of n-steps
steps.
Obviously, it does not need an on-complete
function.
Returns a transition endlessly repeating cycles of `n-steps` steps. Obviously, it does not need an `on-complete` function.
(last-step first-step n-steps)
Simplify provides the last step of a transition given its first-step and the number of steps it lasts.
Simplify provides the last step of a transition given its first-step and the number of steps it lasts.
(merge-transitions state transitions)
Deep merges the provided - often nested - map of transitions in the given state.
Very useful for adding or removing several transitions at once. Indeed, nil values are simply removed when moving the state.
Deep merges the provided - often nested - map of transitions in the given state. Very useful for adding or removing several transitions at once. Indeed, nil values are simply removed when moving the state.
(millis->n-steps millis hz)
Computes the number of steps needed for completing a transition in millis
milliseconds for a phenomenon,
such as the frame-rate, happening hz
per second.
Ex. Computing the number of frames needed in order to last 2000 milliseconds with a frame-rate of 60.
(millis->n-steps 2000
60)
=> 120
Computes the number of steps needed for completing a transition in `millis` milliseconds for a phenomenon, such as the frame-rate, happening `hz` per second. Ex. Computing the number of frames needed in order to last 2000 milliseconds with a frame-rate of 60. (millis->n-steps 2000 60) => 120
(move state step)
Moves a state to the given step (ie. returns a new state representing the given state at the given step).
It belongs to the user to ensure steps are not skipped if it is the needed behavior.
Moves a state to the given step (ie. returns a new state representing the given state at the given step). It belongs to the user to ensure steps are not skipped if it is the needed behavior.
(move-events state step-seq events handle-event)
Merely moving a state is often not enough. Typically, some events happen which modify the state in some way, add, remove, or replace transitions. This function does exactly that.
It behaves like move-seq
in that it moves an initial state following a sequence of steps. However, the state is also modified
by following a sequence of events and an event handler.
An event is an arbitrary map which describes something happening at some arbitrary step. As such, it must have a step associated
under the step-key
key. Events are assumed to be sorted in chronological order (ie. by step).
handle-event
takes the current state and an event, it must return a new state. It is called everytime an event happens.
Like move-seq
, returns a lazy sequence of [state' step].
Merely moving a state is often not enough. Typically, some events happen which modify the state in some way, add, remove, or replace transitions. This function does exactly that. It behaves like `move-seq` in that it moves an initial state following a sequence of steps. However, the state is also modified by following a sequence of events and an event handler. An event is an arbitrary map which describes something happening at some arbitrary step. As such, it must have a step associated under the `step-key` key. Events are assumed to be sorted in chronological order (ie. by step). `handle-event` takes the current state and an event, it must return a new state. It is called everytime an event happens. Like `move-seq`, returns a lazy sequence of [state' step].
(move-seq state step-seq)
Returns a lazy sequence of [state' step] by iteratively moving the given state following the given sequence of steps.
Stops as soon as it reaches the end of step-seq
or it detects there no more transitions, meaning it is useless to
continue.
Returns a lazy sequence of [state' step] by iteratively moving the given state following the given sequence of steps. Stops as soon as it reaches the end of `step-seq` or it detects there no more transitions, meaning it is useless to continue.
(once first-step n-steps on-step)
(once first-step n-steps on-step on-complete)
Returns a transition lasting n-steps
steps.
Returns a transition lasting `n-steps` steps.
(pipe-complete on-completes)
Given a collection of on-complete
functions, returns an on-complete
function piping arguments into this collection.
Nil values are simply filtered-out.
Given a collection of `on-complete` functions, returns an `on-complete` function piping arguments into this collection. Nil values are simply filtered-out.
(poly state first-step fn-transitions)
(poly state first-step fn-transitions on-complete)
Returns a poly-transition which will follow a collection of functions producing transitions.
Those functions will be provided with the state at the moment the transition is created, the first-step
of this transition and an on-complete
function which must not be ignored. This on-complete
function
ensures that the next transition, if there is one, will be created.
Returns a poly-transition which will follow a collection of functions producing transitions. Those functions will be provided with the state at the moment the transition is created, the first-step of this transition and an `on-complete` function which must not be ignored. This `on-complete` function ensures that the next transition, if there is one, will be created.
(poly-infinite state first-step fn-transitions)
Union of infinite
and poly
. Returns a poly-transition endlessly repeating.
Union of `infinite` and `poly`. Returns a poly-transition endlessly repeating.
(poly-repeating state first-step n-times fn-transitions)
(poly-repeating state first-step n-times fn-transitions on-complete)
Union of repeating
and poly
. Returns a poly-transition repeating n-times
.
Union of `repeating` and `poly`. Returns a poly-transition repeating `n-times`.
(remove-data state data-path)
(remove-data state data-path _percent)
(remove-data state data-path _completion-step _step)
Uses dissoc-in
for removing what is at some data-path.
More useful when used as an on-complete
function and the data needs to be cleaned once the transition completes.
Uses `dissoc-in` for removing what is at some data-path. More useful when used as an `on-complete` function and the data needs to be cleaned once the transition completes.
(remove-pre-data state data-path)
(remove-pre-data state data-path _percent)
(remove-pre-data state data-path _completion-step _step)
A vast majority of modeling involves some form of entities. It is also very common for such entities to be removed once all
their transitions completes, meaning they cannot evolve anymore. This function, used as an on-complete
function, does exactly that.
For instance, modeling asteroids as {:asteroids {42 {:x 542 :y 1000}}} having :x and :y transitions.
Once it cannot move anymore, an asteroid must be cleaned (ie. removed from the state). By providing this function as an on-complete
function to every :x and :y transition garantees that. It will use dissoc-in
for removing [:asteroids 42] once it does not have
any transitions anymore.
A vast majority of modeling involves some form of entities. It is also very common for such entities to be removed once all their transitions completes, meaning they cannot evolve anymore. This function, used as an `on-complete` function, does exactly that. For instance, modeling asteroids as {:asteroids {42 {:x 542 :y 1000}}} having :x and :y transitions. Once it cannot move anymore, an asteroid must be cleaned (ie. removed from the state). By providing this function as an `on-complete` function to every :x and :y transition garantees that. It will use `dissoc-in` for removing [:asteroids 42] once it does not have any transitions anymore.
(remove-transition state data-path)
(remove-transition state data-path _percent)
(remove-transition state data-path _completion-step _step)
Removes a transition given the data-path.
Removes a transition given the data-path.
(repeating first-step n-times n-steps on-step)
(repeating first-step n-times n-steps on-step on-complete)
Returns a transition repeating n-steps
steps n-times
times.
Returns a transition repeating `n-steps` steps `n-times` times.
(scale scaled-a scaled-b percent)
(scale scaled-a scaled-b a b x)
3 args : scales a percent
value to a value between scaled-a
and scaled-b
.
5 args : scales the x
value between a
and b
to be between scaled-a
and scaled-b
.
Ex. (scale 0 1000 0.5)
=> 500
(scale 0
1000
0
100
50)
=> 500
3 args : scales a `percent` value to a value between `scaled-a` and `scaled-b`. 5 args : scales the `x` value between `a` and `b` to be between `scaled-a` and `scaled-b`. Ex. (scale 0 1000 0.5) => 500 (scale 0 1000 0 100 50) => 500
When using move-events
, each event must have a step assigned by this key.
When using `move-events`, each event must have a step assigned by this key.
All transitions belonging to a state must be under this key.
All transitions belonging to a state must be under this key.
(transition-path data-path)
Given a data-path, returns a transition-path
Given a data-path, returns a transition-path
(without-transitions state)
Returns the given state without its transitions.
Returns the given state without its transitions.
cljdoc is a website building & hosting documentation for Clojure/Script libraries
× close