commit f686a8ddb53cfb315d0f4ec4e21f643ffbb696f8 Author: Jeremy Dormitzer Date: Tue Mar 27 09:00:21 2018 -0400 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7c8f5c7 --- /dev/null +++ b/.gitignore @@ -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/ \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..e6c0755 --- /dev/null +++ b/README.md @@ -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 diff --git a/project.clj b/project.clj new file mode 100644 index 0000000..805f03a --- /dev/null +++ b/project.clj @@ -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]}}}) diff --git a/resources/public/index.html b/resources/public/index.html new file mode 100644 index 0000000..f579e0b --- /dev/null +++ b/resources/public/index.html @@ -0,0 +1,10 @@ + + + + + + +
+ + + \ No newline at end of file diff --git a/src_clj/sketchbook/handler.clj b/src_clj/sketchbook/handler.clj new file mode 100644 index 0000000..6892afe --- /dev/null +++ b/src_clj/sketchbook/handler.clj @@ -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 [ app-routes + (wrap-defaults site-defaults) + (wrap-index))) + +(defn -main [& args] + (serve app)) diff --git a/src_clj/sketchbook/random.clj b/src_clj/sketchbook/random.clj new file mode 100644 index 0000000..182e9a4 --- /dev/null +++ b/src_clj/sketchbook/random.clj @@ -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)))) diff --git a/src_clj/sketchbook/sketches.clj b/src_clj/sketchbook/sketches.clj new file mode 100644 index 0000000..0ddd345 --- /dev/null +++ b/src_clj/sketchbook/sketches.clj @@ -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))))) diff --git a/src_clj/sketchbook/sketches/color.clj b/src_clj/sketchbook/sketches/color.clj new file mode 100644 index 0000000..af678bb --- /dev/null +++ b/src_clj/sketchbook/sketches/color.clj @@ -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)) diff --git a/src_cljs/sketchbook/core.cljs b/src_cljs/sketchbook/core.cljs new file mode 100644 index 0000000..29f1151 --- /dev/null +++ b/src_cljs/sketchbook/core.cljs @@ -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")) diff --git a/test/sketchbook/handler_test.clj b/test/sketchbook/handler_test.clj new file mode 100644 index 0000000..00765f1 --- /dev/null +++ b/test/sketchbook/handler_test.clj @@ -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)))))