-
Notifications
You must be signed in to change notification settings - Fork 170
Functional mode (fun mode)
tl;dr: go to example and figure out what fun-mode does by yourself
Functional mode (fun-mode) is a Quil middleware that introduces functional style to Quil. It is available starting from Quil 2.1.0. Initially Quil had no special techniques to deal with state, so you had to come up with your own, like modifying an atom inside draw
function and use the new value as the current state. fun-mode
fixes it by making state explicit and handling it for you.
Fun-mode changes semantics of all handler functions: setup
, draw
, mouse-click
, mouse-wheel
, etc. Here are the changes:
- Value returned from
setup
function is used as initial state. Thus, you should not useset-state!
insidesetup
function. - New
update
function is introduced.update
is a pure function, which takes an old state and returns a new state. It will be called beforedraw
on each frame except the first one. The state returned byupdate
is stored whiledraw
executes, and afterdraw
returns it is passed back to the next invocation ofupdate
. -
draw
function takes state as an argument and draws the passed state. Value returned fromdraw
is ignored. - All other handlers, like mouse and keyboard handlers, take one or two arguments. First argument is an old state. Most handlers receive additional second argument - event object. Value returned from handler is used as a new state.
To enable fun-mode, do the following:
- Update all your functions as described above to take and return arguments.
- Add
quil.middleware/fun-mode
as middleware:
(ns my-sketch
(:require [quil.core :as q]
[quil.middleware :as m]))
...
(q/defsketch fun-sketch
...
:middleware [m/fun-mode])
Some handlers take an additional parameter - event. Example of an event will be provided below each handler.
setup: (fn []) -> initial-state
update: (fn [old-state]) -> new-state
draw: (fn [old-state])
focus-gained: (fn [old-state]) -> new-state
focus-lost: (fn [old-state]) -> new-state
mouse-entered: (fn [old-state event]) -> new-state
{:x 10 :y 20}
mouse-exited: (fn [old-state event]) -> new-state
{:x 10 :y 20}
mouse-pressed: (fn [old-state event]) -> new-state
{:x 10 :y 20
:button :left ; possible values are :left, :right, :center
}
mouse-released: (fn [old-state event]) -> new-state
{:x 10 :y 20}
mouse-clicked: (fn [old-state event]) -> new-state
{:x 10 :y 20
:button :left ; possible values are :left, :right, :center
}
mouse-moved: (fn [old-state event]) -> new-state
{:x 10 :y 20
:p-x 5 :p-y 10 ; p-x and p-y are previous mouse position
}
mouse-dragged: (fn [old-state event]) -> new-state
{:x 10 :y 20
:p-x 5 :p-y 10 ; p-x and p-y are previous mouse position
:button :left ; possible values are :left, :right, :center
}
mouse-wheel: (fn [old-state event]) -> new-state
event is a number, either 1 or -1
key-pressed: (fn [old-state event]) -> new-state
{:key :c ; might be nil, see quil.core/key-as-keyword
:key-code 67 ; see quil.core/key-code
:raw-key \c ; see quil.core/raw-key
}
key-released: (fn [old-state event]) -> new-state
key-typed: (fn [old-state event]) -> new-state
{:key :c ; might be nil, see quil.core/key-as-keyword
:key-code 67 ; see quil.core/key-code
:raw-key \c ; see quil.core/raw-key
}
on-close: (fn [old-state])
It is a Quil middleware which uses only public functions from quil.core
. Take a look at fun_mode.cljc.
Here is a sketch that uses fun-mode. The idea of the sketch is to draw a circle which follows the mouse. It expands on every frame and shrinks when mouse is moved. All functions except draw
are pure functions and draw
only draws (yay!).
(ns fun-mode-sketch
(:require [quil.core :as q]
[quil.middleware :as m]))
(def min-r 10)
(defn setup []
; initial state
{:x 0 :y 0 :r min-r})
(defn update [state]
; increase radius of the circle by 1 on each frame
(update-in state [:r] inc))
(defn draw [state]
(q/background 255)
(q/ellipse (:x state) (:y state) (:r state) (:r state)))
; decrease radius by 1 but keeping it not less than min-r
(defn shrink [r]
(max min-r (dec r)))
(defn mouse-moved [state event]
(-> state
; set circle position to mouse position
(assoc :x (:x event) :y (:y event))
; decrease radius
(update-in [:r] shrink)))
(q/defsketch example
:size [200 200]
:setup setup
:draw draw
:update update
:mouse-moved mouse-moved
:middleware [m/fun-mode])
And result: