Initial commit

This commit is contained in:
Jeremy Dormitzer 2018-03-27 09:00:21 -04:00
commit f686a8ddb5
No known key found for this signature in database
GPG Key ID: 04F17C0F5A32C320
10 changed files with 286 additions and 0 deletions

12
.gitignore vendored Normal file
View File

@ -0,0 +1,12 @@
/target
/lib
/classes
/checkouts
pom.xml
pom.xml.asc
*.jar
*.class
/.lein-*
/.nrepl-port
figwheel_server.log
resources/public/js/generated/

19
README.md Normal file
View File

@ -0,0 +1,19 @@
# sketchbook.jeremydormitzer.com
FIXME
## Prerequisites
You will need [Leiningen][] 2.0.0 or above installed.
[leiningen]: https://github.com/technomancy/leiningen
## Running
To start a web server for the application, run:
lein ring server
## License
Copyright © 2018 FIXME

31
project.clj Normal file
View File

@ -0,0 +1,31 @@
(defproject sketchbook.jeremydormitzer.com "0.1.0-SNAPSHOT"
:description "FIXME: write description"
:url "http://example.com/FIXME"
:min-lein-version "2.0.0"
:source-paths ["src_clj"]
:dependencies [[org.clojure/clojure "1.8.0"]
[compojure "1.6.0"]
[ring/ring-defaults "0.3.1"]
[ring-server "0.5.0"]
[org.apache.commons/commons-math3 "3.6.1"]
[org.clojure/core.async "0.4.474"]
[quil "2.6.0"]
[reagent "0.8.0-alpha2"]]
:plugins [[lein-ring "0.9.7"]
[lein-figwheel "0.5.15"]]
:cljsbuild {:builds [{:id "dev"
:source-paths ["src_cljs"]
:figwheel true
:compiler {:main "sketchbook.core"
:optimizations :none
:asset-path "js/generated/out"
:output-to "resources/public/js/generated/index.js"
:output-dir "resources/public/js/generated/out"}}]}
:ring {:handler sketchbook.handler/app}
:profiles
{:dev {:source-paths ["src_cljs"]
:dependencies [[javax.servlet/servlet-api "2.5"]
[ring/ring-mock "0.3.0"]
[figwheel-sidecar "0.5.8"]
[com.cemerick/piggieback "0.2.2"]]
:repl-options {:nrepl-middleware [cemerick.piggieback/wrap-cljs-repl]}}})

View File

@ -0,0 +1,10 @@
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<div id="app"></div>
<script src="js/generated/index.js"></script>
</body>
</html>

View File

@ -0,0 +1,55 @@
(ns sketchbook.handler
(:require [compojure.core :refer :all]
[compojure.route :as route]
[ring.middleware.defaults :refer [wrap-defaults site-defaults]]
[ring.server.standalone :refer [serve]]
[clojure.core.async :refer [<!! <! go]]
[sketchbook.sketches :as sketches])
(:import [java.io ByteArrayInputStream]))
(defn sketch-handler
[request]
(let [{:keys [sketch seed width height]
:or {seed (str (System/nanoTime)),
width "512",
height "512"}} (:params request)
sketch-bytes (<!!
(sketches/render-sketch
(keyword sketch)
(Long/parseLong seed)
[(Long/parseLong width) (Long/parseLong height)]))
byte-stream (ByteArrayInputStream. sketch-bytes)]
{:status 200
:headers {"Content-Type" "image/png"}
:body byte-stream})
#_[request respond raise]
#_(go
(let [{:keys [sketch seed width height] :or {seed (System/nanoTime)}} (:params request)
sketch-bytes (<!
(sketches/render-sketch
(keyword sketch)
(Long/parseLong seed)
[(Long/parseLong width) (Long/parseLong height)]))
byte-stream (ByteArrayInputStream. sketch-bytes)]
(respond {:status 200
:headers {"Content-Type" "image/tiff"}
:body byte-stream}))))
(defroutes app-routes
(context "/api" []
(GET "/sketches/:sketch" [sketch width height] sketch-handler)
(GET "/sketches/:sketch/:seed" [sketch seed width height] sketch-handler))
(route/resources "/")
(route/not-found "Not Found"))
(defn wrap-index [handler]
(fn [request]
(handler (update-in request [:uri] #(if (= "/" %) "/index.html" %)))))
(def app
(-> app-routes
(wrap-defaults site-defaults)
(wrap-index)))
(defn -main [& args]
(serve app))

View File

@ -0,0 +1,19 @@
(ns sketchbook.random
(:import [org.apache.commons.math3.random ISAACRandom]
[org.apache.commons.math3.distribution NormalDistribution]))
(def rng (ISAACRandom.))
(defn set-seed [seed]
(.setSeed rng seed))
(defn random
([max] (random 0 max))
([min max]
(+ (* (.nextFloat rng) (- max min)) min)))
(defn random-normal
([] (random-normal 0 1))
([mean dev]
(let [distribution (NormalDistribution. rng (double mean) (double dev))]
(.sample distribution))))

View File

@ -0,0 +1,43 @@
(ns sketchbook.sketches
(:require [quil.core :as q]
[clojure.java.io :as io]
[clojure.core.async :refer [chan go >!]]
[sketchbook.random :as r]
[sketchbook.sketches.color])
(:import [java.io ByteArrayOutputStream]))
(def sketches
{:color {:setup sketchbook.sketches.color/setup
:draw sketchbook.sketches.color/draw}})
(defn sketch-bytes [& {:keys [size setup draw seed]}]
(let [channel (chan)
nonce (str (System/currentTimeMillis))
filename (str "sketch-" nonce ".png")
out (ByteArrayOutputStream.)]
(q/sketch :size size
:setup (fn []
(r/set-seed seed)
(setup))
:draw (fn []
(draw)
(q/save filename)
(with-open [in (io/input-stream filename)]
(io/copy in out))
(io/delete-file filename)
(go (>! channel (.toByteArray out))
(q/exit))))
channel))
(defn render-sketch
"Returns a channel that resolves with the bytes of the rendered sketch.
`name` is the name of the sketch as a symbol (i.e. :color),
`seed` is the long to seed the PRNG with, and `size` is a vector
[width, height]."
[name seed size]
(let [sketch (get sketches name)]
(when-not (nil? sketch)
(sketch-bytes :size size
:seed seed
:setup (:setup sketch)
:draw (:draw sketch)))))

View File

@ -0,0 +1,64 @@
(ns sketchbook.sketches.color
(:require [quil.core :as q]
[sketchbook.random :as r]))
(def colors
{:offwhite [40 15 100]
:blue [200 75 70]
:red [10 85 65]})
(defn h
([] (h 1.0))
([n] (* (q/height) n)))
(defn w
([] (w 1.0))
([n] (* (q/width) n)))
(defn setup []
(q/color-mode :hsb 360 100 100)
(q/no-loop))
(defn draw-circles
"Draws `count` circles of colors close to `color`.
The circles are distributed around `x-target`, `y-target`
by a normal distribution with deviation `d-dev`. The radius
of each circle is determined by sampling a normal distribution
defined by `r-mean` and `r-dev`"
[x-target y-target d-dev r-mean r-dev count color]
(doseq [i (range 0 count)]
(let [r (r/random-normal r-mean r-dev)
x (r/random-normal x-target d-dev)
y (r/random-normal y-target d-dev)
color (assoc color
0
(r/random (- (first color) 5)
(+ (first color) 5)))]
(apply q/fill color)
(q/ellipse x y r r))))
(defn flip-randomly [prob max num]
(if (< (r/random 1.0) prob)
(- max num)
num))
(defn draw []
(apply q/background (:offwhite colors))
(let [x-target (flip-randomly 0.5 (w) (r/random (w 0.25) (w 0.40)))
y-target (flip-randomly 0.5 (h) (r/random (h 0.25) (h 0.40)))]
(draw-circles x-target
y-target
(r/random (w 0.20) (w 0.30))
(w 0.02)
(w 0.02)
(r/random-normal 650 50)
(:blue colors))
(draw-circles (- (w) x-target)
(- (h) y-target)
(r/random (w 0.20) (w 0.30))
(w 0.02)
(w 0.02)
(r/random-normal 650 50)
(:red colors))))
(defn render [] (q/sketch :size [768 768] :setup setup :draw draw))

View File

@ -0,0 +1,19 @@
(ns sketchbook.core
(:require [reagent.core :as r]))
(defonce state (r/atom {:thing "world"}))
(defn hello []
[:div
[:h1 (str "Hello " (:thing @state)"!")]
[:button {:onClick (fn [] (swap! state #(assoc % :thing "universe")))}
"Universe"]
[:button {:onClick (fn [] (swap! state #(assoc % :thing "world")))}
"World"]])
(defn app []
[:div
[hello]])
(r/render [app]
(.getElementById js/document "app"))

View File

@ -0,0 +1,14 @@
(ns sketchbook.handler-test
(:require [clojure.test :refer :all]
[ring.mock.request :as mock]
[sketchbook.handler :refer :all]))
(deftest test-app
(testing "main route"
(let [response (app (mock/request :get "/"))]
(is (= (:status response) 200))
(is (= (:body response) "Hello World"))))
(testing "not-found route"
(let [response (app (mock/request :get "/invalid"))]
(is (= (:status response) 404)))))