block by dribnet 5063746

histo-grow

Full Screen

Summing uniform random numbers approximates a gaussian. Keep summing and it gets ever smoother, right? Wrong.

index.html

<!DOCTYPE html>
<meta charset="utf-8">
<title>histo-grow</title>
<style>

body {
  font: 10px sans-serif;
  background: whitesmoke;
}

.bar rect {
  fill: firebrick;
  shape-rendering: crispEdges;
}

.bar text {
  fill: #fff;
}

.axis path, .axis line {
  fill: none;
  stroke: #000;
  shape-rendering: crispEdges;
}

</style>

<body>
  <script type="text/javascript" src="//d3js.org/d3.v3.min.js"></script>
  <script type="text/javascript" src="histo-grow.js"></script>
</body>

histo-grow.cljs

(ns histo-grow
  (:require [strokes :refer [d3]]))

; debug helper
(defn dp [& args]
  (.log js/console (apply str args)) )

(strokes/bootstrap)

(defn values[]
  (vec (map (partial * 100)
    (take 1000 (repeatedly 
      ; (-> d3 .-random (.logNormal)))))))
      #(.random js/Math))))))
      ; (-> d3 .-random (.normal)))))))
      ; (-> d3 .-random (.irwinHall 10)))))))

(def format-count (-> d3 (.format ",.0f")))

(def margin {:top 10 :right 30 :bottom 30 :left 30})
(def width  (- 960 (:left margin) (:right margin)))
(def height (- 500 (:top margin)  (:bottom margin)))

(def svg (-> d3 (.select "body") (.append "svg")
      (.attr {:width  (+ width (:left margin) (:right margin))
              :height (+ height (:top margin) (:bottom margin))})
    (.append "g")
      (.attr "transform" (str "translate(" (:left margin) "," 
                            (:top margin) ")"))))

(-> svg (.append "g")
  (.attr {:class "x axis"
          :transform (str "translate(0," height ")")}))

(def highest-y (atom 0))

(defn update [values]
  (let [[x1 x2] ((juxt #(apply min %) #(apply max %)) values)
        slop  (* (- x2 x1) 0.1)
        [xmin xmax] [(- x1 slop) (+ x2 slop)]
        x     (-> d3 .-scale (.linear)
                (.domain [xmin xmax])
                (.range  [0 width]))

        x-axis (-> d3 .-svg (.axis)
                 (.scale x)
                 (.orient "bottom"))

        data  ((-> d3 .-layout (.histogram)
                    (.bins 50))
                values) ; tricky function call

        ymax  (apply max (map #(aget % "y") data))
        ystop (if (> ymax @highest-y) (reset! highest-y ymax) @highest-y)
        y     (-> d3 .-scale (.linear)
                (.domain [0 ystop])
                (.range [height 0]))

        bar   (-> svg (.selectAll ".bar")
                  (.data data #(identity %2)))

        newbs (-> bar (.enter) (.append "g"))

        bar-map {:class "bar"
                 :transform 
                   #(str "translate(" (x (aget % "x")) "," 
                                      (y (aget % "y")) ")")}

        bar-width (x (+ xmin (aget (first data) "dx")))

        rect-map {:x 1
                  :width (- bar-width 1)
                  :height #(- (+ 0 height) (y (aget % "y")))}]

    ; only append on the enter selection
    (-> newbs (.attr bar-map))
    (-> newbs (.append "rect")
      (.transition)
        (.duration 250) 
        (.attr rect-map))
    (-> newbs (.append "text"))

    (-> bar
      (.transition)
        (.duration 250) 
        (.attr bar-map))

    ; update others
    (-> bar (.select "rect")
      (.transition)
        (.duration 250)
        (.attr rect-map))

    (-> bar (.select "text")
      (.attr {:dy ".75em"
              :y  6
              :x  (/ bar-width 2)
              :text-anchor "middle"})
      (.text #(format-count (aget % "y"))))

    (-> bar (.exit)
      (.transition)
        (.duration 250)
        (.style {:fill-opacity 1e-6})
        (.remove))

    (-> svg (.select ".x.axis")
      (.transition)
        (.duration 250)
        (.call x-axis))
))

(def summed-values (atom (values)))
(update @summed-values)

(.setInterval js/window (fn []
  (swap! summed-values #(vec (map + % (values))))
  ; (swap! summed-values #(vec (concat % (values))))
  (update @summed-values))
  ; 2 seconds between swaps
  2000)