ClojureScript Demo : Convex Hull
Update : bug-fix when hull was being incorrectly calculated due to there being duplicate points generated in the random set.
This was a piece of cake. I really like the pre-compiled approach that relies on the Closure compiler/library. It just feels like you’re writing a regular application instead of trying to force the browser to do the ‘correct’ thing with run-time code and the DOM. There are a few differences that I ran into, a few functions don’t yet exist and using macros is not as clean as I’d expect. Macros have to be implemented in Clojure and then referenced from ClojureScript. No big deal really.
Here’s all the UI code. Not much really at < 100 lines. Very cool.
(def edge-stroke (graphics/Stroke. 1 "#444")) (def blue-edge-stroke (graphics/Stroke. 1 "#66b")) (def green-edge-stroke (graphics/Stroke. 1 "#0f0")) (def white-fill (graphics/SolidFill. "#fff")) (def blue-fill (graphics/SolidFill. "#66b")) (def green-fill (graphics/SolidFill. "#0f0")) (def trans-fill (graphics/SolidFill. "#0f0" 0.001)) (def g (doto (graphics/createGraphics "440" "440") (.render (dom/getElement "graph")))) (defn draw-graph  (let [canvas-size (. g (getPixelSize))] (.drawRect g 0 0 (.width canvas-size) (.height canvas-size) edge-stroke white-fill))) (defn scale-coord [coord] (+ 20 (* 4 coord))) (defn draw-points [points stroke fill] (doseq [[x y :as pt] points] (.drawEllipse g (scale-coord x) (scale-coord y) 3 3 stroke fill))) (defn draw-convex-hull [points stroke fill] (let [path (graphics/Path.) [xs ys :as start] (first points)] (.moveTo path (scale-coord xs) (scale-coord ys)) (doall (map (fn [[x y :as pt]] (.lineTo path (scale-coord x) (scale-coord y))) (rest points))) (.lineTo path (scale-coord xs) (scale-coord ys)) (.drawPath g path stroke fill))) (defn print-points [points el] (doseq [pair points] (dom/append el (str " [" (first pair) " " (second pair) "]")))) (defn ^:export rundemo  (let [cnt 1E2 rpts (apply vector (map (fn [n] [(rand-int (inc cnt)) (rand-int (inc cnt))]) (range 1 cnt ))) text-input-title (dom/getElement "text-input-title") text-input (dom/getElement "text-input") text-results-status (dom/getElement "text-results-status") text-results (dom/getElement "text-results")] (draw-graph) ;; draw all points (dom/setTextContent text-input-title (str "Random generation of " cnt " points...")) (draw-points rpts blue-edge-stroke blue-fill) (print-points rpts text-input) ;; calc hull (dom/setTextContent text-results-status (str "Calculating convex hull ...")) (let [r1 (randomset rpts false) r2 (randomset rpts true)] (dom/append text-results-status (str " done.n")) ;; update the results (print-points r2 text-results) (dom/append text-results-status (str "Convex hull has " (count r1) " points.n")) ;; draw hull points (draw-points r1 green-edge-stroke green-fill) ;; draw hull (draw-convex-hull r1 green-edge-stroke trans-fill) ;; return the results [rpts r2]))) ;; Auto-update (defn ^:export poll  (let [timer (goog.Timer. 15000)] (do (rundemo) (. timer (start)) (events/listen timer goog.Timer/TICK rundemo))))
The future of client-side programming just got way better thanks to Rich and team !
All code is here.