https://github.com/rm-hull/big-bang
ClojureScript event loop abstraction, loosely based on Racket's big-bang and implemented on top of core.async
https://github.com/rm-hull/big-bang
Last synced: over 1 year ago
JSON representation
ClojureScript event loop abstraction, loosely based on Racket's big-bang and implemented on top of core.async
- Host: GitHub
- URL: https://github.com/rm-hull/big-bang
- Owner: rm-hull
- License: mit
- Created: 2014-01-12T11:11:58.000Z (over 12 years ago)
- Default Branch: master
- Last Pushed: 2014-11-23T22:25:06.000Z (over 11 years ago)
- Last Synced: 2024-10-03T12:18:01.791Z (almost 2 years ago)
- Language: Clojure
- Homepage:
- Size: 1020 KB
- Stars: 65
- Watchers: 6
- Forks: 8
- Open Issues: 5
-
Metadata Files:
- Readme: README.md
- License: LICENSE.md
Awesome Lists containing this project
README
# Big-Bang [](http://travis-ci.org/rm-hull/big-bang)
Big-Bang is a ClojureScript game-loop / event-loop abstraction, loosely based on
Racket's [big-bang][a] and implemented on top of [core.async][b]. It is a pure
ClojureScript implementation with no external Javascript dependencies. Using
_Big-Bang_ encourages you to implement what would be otherwise stateful code
in a pure functional manner; of course, inevitably, at some point you have to
punch outside and twiddle some IO or paint some pixels - this can be entirely
encapsulated in the render handler however.
See [Pacman](http://rm-hull.github.io/big-bang/example.html) (work-in-progress), a
[simple animation](http://programming-enchiladas.destructuring-bind.org/rm-hull/8623502), a
[tumbling 3D torus](http://programming-enchiladas.destructuring-bind.org/rm-hull/7098992),
[parametric equations](http://programming-enchiladas.destructuring-bind.org/rm-hull/8776719), and
[Rock Paper Scissors](http://programming-enchiladas.destructuring-bind.org/rm-hull/8723389)
for demos, and for a code comparison between Big-Bang and [OM][c], see here:
[Om mouse move][d] vs. [Big Bang mouse move][e] ツ
[a]: http://docs.racket-lang.org/teachpack/2htdpuniverse.html#(form._world._((lib._2htdp/universe..rkt)._big-bang))
[b]: http://github.com/clojure/core-async
[c]: https://github.com/swannodette/om
[d]: http://programming-enchiladas.destructuring-bind.org/rm-hull/8617445
[e]: http://programming-enchiladas.destructuring-bind.org/rm-hull/8617788
### Pre-requisites
You will need [Leiningen](https://github.com/technomancy/leiningen) 2.3.4 or
above installed.
### Building & Testing
To build and install the library locally, run:
$ lein cljsbuild once
$ lein install
To test against [PhantomJS](http://phantomjs.org/), ensure that that
package is installed properly, and run:
$ lein cljsbuild test
Alternatively, open ```resources/run-tests.html``` in a browser - this
executes the tests and the test results are displayed on the page.
### Including in your project
There is an 'alpha-quality' version hosted at [Clojars](https://clojars.org/rm-hull/big-bang).
For leiningen include a dependency:
```clojure
[rm-hull/big-bang "0.0.1-SNAPSHOT"]
```
For maven-based projects, add the following to your `pom.xml`:
```xml
rm-hull
big-bang
0.0.1-SNAPSHOT
```
## Basic Usage
Big bang takes a number of keyword arguments in its call, most of which are optional;
only ```:initial-state``` and ```:to-draw``` are required.
The ```:initial-state``` value should be a persistent data-structure which typically
means using a map or vector. It should be the basis of the starting state for the component.
Event handlers can be attached by adding a key starting with ```:on-...``` (for
example ```:on-keydown```, ```:on-click```) and a value of a function handler
reference: the function should take two arguments: an event and a world-state,
and return a (possibly modified) world state. The event naming follows typical
javascript event types, and unless a DOM element is specified in ```:event-target```,
the events are bound to _document.body_. If you find yourself wanting to bind to
multiple events from different targets within a single big-bang world, this is a
clear sign that you should be using multiple (smaller) big-bang components instead.
**NOTE:** There are two reserved event handlers:
* ```:on-tick``` which if present, invokes an interval timer every 17ms,
(i.e. an effective frame rate of approx 60 frames/sec) or whatever rate
is defined by ```:tick-rate```. If not defined, then no timer event
source will be installed,
* ```:on-receive``` which is a handler that is invoked
when external messages are received (on the ```:receive-channel```).
On start-up, all the event sources are assembled and any initialization occurs.
Inside a go loop, the associated channels (/ports) are passed to a _core.async_
alts! method. The resulting value ('the received event') is then dispatched to
the relevant handler function along with the world-state.
It is _part of the expected contract between big-bang and the event handlers_ that:
* The handler function accepts an event and world-state as arguments, and that
a modified world-state is returned. The world state can be packaged to
include a message which will be emitted on the ```:send-channel``` - just
return ```(make-package modified-world-state message)``` instead.
* To take advantage of structural sharing, rather than creating new states,
you are encouraged to use ```assoc```, ```assoc-in```, ```merge``` or
```update-in``` to modify the existing world state. The threading macros
(```->``` and ```->>```) are particularly useful constructs for making this
intent clear.
* The handler function _should_ generally be idempotent and free from side effects:
the event and the incoming world-state are the only things that will effect the
returned world-state.
The new world-state is compared to the old world-state, and if
there is a difference, then the ```:to-draw``` renderer is invoked. The renderer
handler accepts a single argument: the world-state. It should completely render
the component to the DOM according to the supplied world-state, whether this is
canvas operations or via some DOM manipulation library such as
[Dommy](https://github.com/Prismatic/dommy).
Big-bang can be terminated by supplying a ```:stop-when?``` handler (accepting a
single argument of the world-state, returning true will cease the event loop), or
after a fixed number of frames specified by the ```:max-frames``` value. By default,
it will run indefinitely however.
### A simple example
The following code sample gives a simple high level overview of how to program
a big-bang world. See http://programming-enchiladas.destructuring-bind.org/rm-hull/8623502
for a running demo of this code:
```clojure
(ns big-bang.example.cat-animation
(:require-macros [cljs.core.async.macros :refer [go]])
(:require [enchilada :refer [canvas ctx canvas-size proxy-request]]
[cljs.core.async :as async :refer [