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