diff --git a/src/cljs/vcfvis/data.cljs b/src/cljs/vcfvis/data.cljs index a6e1612..9e8c73f 100644 --- a/src/cljs/vcfvis/data.cljs +++ b/src/cljs/vcfvis/data.cljs @@ -3,33 +3,69 @@ [reflex.macros :only [constrain!]]) (:use [c2.util :only [clj->js]] [cljs.reader :only [read-string]]) - (:require [vcfvis.core :as core])) + (:require [vcfvis.core :as core] + [vcfvis.ui :as ui] + [c2.scale :as scale] + [c2.ticks :as ticks])) (def num-bins 100) -(defn expand-metric [metric-id] - (if-let [m (-> @core/!context :metrics metric-id)] - (merge {:id metric-id} m) - (throw (str "No metric information for metric-id: " metric-id)))) +(defn expand-metric + "Adds id and an x-scale with tick marks to a metric." + [metric] + (if (metric :range) + (assoc metric + :scale-x (let [{:keys [ticks]} (ticks/search (metric :range) + :clamp? true :length ui/hist-width) + x (scale/linear :domain (metric :range) + :range [0 ui/hist-width])] + (assoc x :ticks ticks))) + (throw (str "Metric doesn't have range: " (pr metric))))) + +(defn prep-context [context] + (update-in context [:metrics] + #(reduce (fn [res [id m]] + (assoc res id + (-> m + expand-metric + (assoc :id id)))) + {} %))) (defn prep-vcf-json [vcf-json] - (let [info (read-string (aget vcf-json "clj")) + (let [info (-> (read-string (aget vcf-json "clj")) + ;;Expand metric-ids to full metric maps available in context (a join, basically) + (update-in [:available-metrics] #(map (@core/!context :metrics) %))) + cf (js/crossfilter (aget vcf-json "raw"))] - (merge (update-in info [:available-metrics] #(set (map expand-metric %))) - {:cf (into {:crossfilter cf} - (for [metric (info :available-metrics)] - (let [[start end] (get-in @core/!context [:metrics metric :range]) - bin-width (/ (- end start) num-bins) - dim (.dimension cf #(aget % metric)) - binned (.group dim (fn [x] - (+ start (* bin-width - ;;take the min to catch any roundoff into the last bin - (min (Math/floor (/ (- x start) bin-width)) - (dec num-bins))))))] - [metric {:bin-width bin-width - :dimension dim - :binned binned}])))}))) + + (assoc info + :cf (into {:crossfilter cf} + (for [{:keys [id range]} (info :available-metrics)] + (let [[start end] range + bin-width (/ (- end start) num-bins) + dim (.dimension cf #(aget % id)) + binned (.group dim (fn [x] + (+ start (* bin-width + ;;take the min to catch any roundoff into the last bin + (min (Math/floor (/ (- x start) bin-width)) + (dec num-bins))))))] + [id {:bin-width bin-width + :dimension dim + :binned binned}])))))) + + + + + + + + + + + + + diff --git a/src/cljs/vcfvis/histogram.cljs b/src/cljs/vcfvis/histogram.cljs index 582d2d4..56a6b52 100644 --- a/src/cljs/vcfvis/histogram.cljs +++ b/src/cljs/vcfvis/histogram.cljs @@ -5,26 +5,20 @@ [c2.maths :only [irange extent]]) (:require [vcfvis.core :as core] [vcfvis.data :as data] + [vcfvis.ui :as ui] [vcfvis.double-range :as double-range] [c2.dom :as dom] [c2.event :as event] [c2.scale :as scale] [c2.svg :as svg] - [c2.ticks :as ticks] [goog.string :as gstr])) -(def margin "left/right margin" 20) -(def inter-hist-margin "vertical margin between stacked histograms" 20) -(def axis-height (js/parseFloat (dom/style "#hist-axis" :height))) +(def height ui/hist-height) +(def width ui/hist-width) +(def margin ui/hist-margin) +(def inter-hist-margin ui/inter-hist-margin) +(def axis-height ui/axis-height) -(def height - "height available to histogram facet grid" - (js/parseFloat (dom/style "#histograms" :height))) - -(def width - "Width of histogram facet grid" - (- (js/parseFloat (dom/style "#histograms" :width)) - (* 2 margin))) ;; (def !selected-extent (atom [0 1])) @@ -53,28 +47,26 @@ ;; (fn [_ _ _ _] (data/reset-statuses!))) - - -(defn histogram* [vcf scale-x metric] +(defn hist-svg* [vcf metric & {:keys [margin]}] (let [height (- height axis-height) - {:keys [dimension binned bin-width]} (get-in vcf [:cf (metric :id)]) + {metric-id :id scale-x :scale-x} metric + {:keys [dimension binned bin-width]} (get-in vcf [:cf metric-id]) + ;;Since we're only interested in relative density, histograms have free y-scales. scale-y (scale/linear :domain [0 (aget (first (.top binned 1)) "value")] :range [0 height]) dx (- (scale-x bin-width) (scale-x 0))] - [:div.histogram - [:span.label (vcf :file-url)] + [:svg {:width (+ width (* 2 margin)) :height (+ height inter-hist-margin)} + [:g {:transform (svg/translate [margin margin])} + [:g.distribution {:transform (str (svg/translate [0 height]) + (svg/scale [1 -1]))} - [:svg {:width (+ width (* 2 margin)) :height (+ height inter-hist-margin)} - [:g {:transform (svg/translate [margin margin])} - [:g.distribution {:transform (str (svg/translate [0 height]) - (svg/scale [1 -1]))} - (for [d (.all binned)] - (let [x (aget d "key"), count (aget d "value")] - [:rect.bar {:x (scale-x x) - :width (- (scale-x (+ x bin-width)) - (scale-x x)) - :height (scale-y count)}]))]]]])) + (for [d (.all binned)] + (let [x (aget d "key"), count (aget d "value")] + [:rect.bar {:x (scale-x x) + :width (- (scale-x (+ x bin-width)) + (scale-x x)) + :height (scale-y count)}]))]]])) @@ -82,24 +74,23 @@ (bind! "#main-hist" (let [vcfs @core/!vcfs] (if (seq vcfs) - (let [metric @core/!metric - metric-extent (metric :range) - {:keys [ticks]} (ticks/search metric-extent - :clamp? true :length width) - x (scale/linear :domain metric-extent - :range [0 width])] + (let [{x :scale-x :as metric} @core/!metric] [:div#main-hist [:div#histograms - - ;;histogram distributions - (histogram* (first vcfs) x metric)] + (for [vcf vcfs] + [:div.histogram + [:span.label (vcf :file-url)] + (hist-svg* vcf metric :margin margin)])] [:div#hist-axis [:div.axis.abscissa [:svg {:width (+ width (* 2 margin)) :height axis-height} [:g {:transform (svg/translate [margin 2])} - (svg/axis x ticks :orientation :bottom + (svg/axis x (:ticks x) + :orientation :bottom :formatter (partial gstr/format "%.1f"))]]]]]) + + ;;If no VCFs, clear everything [:div#main-hist [:div#histograms] diff --git a/src/cljs/vcfvis/stub.cljs b/src/cljs/vcfvis/stub.cljs index 351a23f..91423e5 100644 --- a/src/cljs/vcfvis/stub.cljs +++ b/src/cljs/vcfvis/stub.cljs @@ -31,7 +31,7 @@ (defn load-context [callback] (timeout 10 - (callback context))) + (callback (data/prep-context context)))) (defn load-vcf [file-url callback] (.getJSON js/$ "big.json" diff --git a/src/cljs/vcfvis/ui.cljs b/src/cljs/vcfvis/ui.cljs index 2bbd468..cc1459a 100644 --- a/src/cljs/vcfvis/ui.cljs +++ b/src/cljs/vcfvis/ui.cljs @@ -7,3 +7,23 @@ (let [$dd (dom/select "#user-dropdown")] (constrain! (dom/text $dd (get @core/!context :username "USER")))) + + + + + +;;;;;;;;;;;;;;;;;;; +;;Histogram params + +(def hist-margin "left/right margin" 20) +(def inter-hist-margin "vertical margin between stacked histograms" 20) +(def axis-height (js/parseFloat (dom/style "#hist-axis" :height))) + +(def hist-height + "Height available to histogram facet grid" + (js/parseFloat (dom/style "#histograms" :height))) + +(def hist-width + "Width of histogram facet grid" + (- (js/parseFloat (dom/style "#histograms" :width)) + (* 2 hist-margin)))