Initial commit
This commit is contained in:
commit
f686a8ddb5
12
.gitignore
vendored
Normal file
12
.gitignore
vendored
Normal 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
19
README.md
Normal 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
31
project.clj
Normal 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]}}})
|
10
resources/public/index.html
Normal file
10
resources/public/index.html
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
<script src="js/generated/index.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
55
src_clj/sketchbook/handler.clj
Normal file
55
src_clj/sketchbook/handler.clj
Normal 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))
|
19
src_clj/sketchbook/random.clj
Normal file
19
src_clj/sketchbook/random.clj
Normal 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))))
|
43
src_clj/sketchbook/sketches.clj
Normal file
43
src_clj/sketchbook/sketches.clj
Normal 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)))))
|
64
src_clj/sketchbook/sketches/color.clj
Normal file
64
src_clj/sketchbook/sketches/color.clj
Normal 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))
|
19
src_cljs/sketchbook/core.cljs
Normal file
19
src_cljs/sketchbook/core.cljs
Normal 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"))
|
14
test/sketchbook/handler_test.clj
Normal file
14
test/sketchbook/handler_test.clj
Normal 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)))))
|
Loading…
Reference in New Issue
Block a user