Skip to content

Instantly share code, notes, and snippets.

@nbeloglazov
Created April 5, 2017 06:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nbeloglazov/0828efd3b674bdf587e4a12f83729432 to your computer and use it in GitHub Desktop.
Save nbeloglazov/0828efd3b674bdf587e4a12f83729432 to your computer and use it in GitHub Desktop.
Example of using long-running background tasks with Quil. Quil's UI thread only displays data while background task perform CPU-intensive work (multiplying matrices) without blocking UI thread.
(ns longtask.core
(:require [quil.core :as q]
[quil.middleware :as m]))
; This program shows an example how to integrate long-running
; background tasks into Quil sketch. Clicking on the left or
; right half of the screen starts a new background task which does following:
; 1. Generates random square matrix of size rand of [100, 200, 500, 1000]
; 2. Multiplies matrix by itself. During the multiplication background thread
; updates (:progress state) value so Quil can display the progress.
; 3. Once the multiplication is done thread updates (:messages state)
; list to append "finished" message.
;
; This example supports 2 parallel background tasks at the same time.
; State object used by communicate data from background tasks to
; Quil :draw function. Tasks update :progress and :messages fields
; while :draw function prints them in sketch.
(def state
(atom
{:progress [0 0]
:messages [[] []]
:futures [(future) (future)]
:highlighted nil}))
(defn rand-matrix
"Generates matrix NxN with random values from 0 to 100."
[n]
(let [rand-row #(repeatedly n (partial rand-int 100))]
(repeatedly n rand-row)))
(defn scalar-mult [a b]
(apply + (map * a b)))
(defn transpose [m]
(apply mapv vector m))
(defn update-progress [ind size row]
(let [progress (quot (* 100 row) size)]
(swap! state assoc-in [:progress ind] progress)))
(defn append-message [ind message]
(swap! state update-in [:messages ind] #(conj % message)))
(defn matrix-squared
"Multiplies matrix A by B. ind is index of state to update.
This function doesn't return anything so result matrix is
lost. The only purpose of this function is to run some
CPU-intensive computation and update progress."
[a ind]
(let [n (count a)
transp-a (doall (transpose a))]
(dotimes [r n]
(update-progress ind n r)
(dotimes [c n]
(scalar-mult (nth a r) (nth transp-a c))))))
(defn run-task
"Runs task of calculating square of a random matrix."
[ind]
(let [n (rand-nth [100 200 500 1000])]
(append-message ind (str "starting, size " n))
(matrix-squared (rand-matrix n) ind)
(append-message ind (str "finished"))))
(defn launch-task
"Launches task of calculating square of a random matrix.
This function starts task in a new thread."
[ind]
(let [prev-task (get-in @state [:futures ind])]
(if (future-done? prev-task)
(let [new-task (future (run-task ind))]
(swap! state assoc-in [:futures ind] new-task))
(append-message ind "previous task still running"))))
(defn draw []
(let [{:keys [progress messages highlighted]} @state]
(q/background 240)
; Split screen into 2 halves, left and right.
; Each half is going to display results of its task.
(doseq [ind [0 1]
:let [progress (nth progress ind)
messages (reverse (nth messages ind))]]
(q/with-translation [(* ind (/ (q/width) 2)) 0]
; Highglight half of the screen where cursors hovering
(when (= highlighted ind)
(q/fill 255 253 208)
(q/rect 0 0 (/ (q/width) 2) (q/height)))
; Print progress of the current task and all its
; messages
(q/fill 0)
(q/text (str "Progress #1: " progress "%") 10 20)
(dotimes [i (count messages)]
(q/text (nth messages i) 10 (+ 40 (* i 15))))))))
(defn half-under-mouse
"Returns 0 if mouse is over the left half of the sketch
end and 1 if it's over the right half."
[]
(if (< (q/mouse-x) (/ (q/width) 2)) 0 1))
(defn mouse-moved []
(swap! state assoc :highlighted (half-under-mouse)))
(defn mouse-clicked []
(launch-task (half-under-mouse)))
(q/defsketch longtask
:title "You spin my circle right round"
:size [500 500]
:draw draw
:features [:keep-on-top]
:mouse-moved mouse-moved
:mouse-clicked mouse-clicked
:middleware [m/pause-on-error])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment