Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
word cloud optimizations and memoization
  • Loading branch information
andrew committed Jun 22, 2016
1 parent 8c99be7 commit 796c20d
Show file tree
Hide file tree
Showing 6 changed files with 91 additions and 90 deletions.
5 changes: 3 additions & 2 deletions src/cljs/ulysses/components/word_cloud.cljs
@@ -1,7 +1,7 @@
(ns ulysses.components.word-cloud
(:require [re-frame.core :refer [subscribe]]
[ulysses.utils :refer [map-subels]]
[ulysses.lib.packer :refer [fit-words]]
[ulysses.lib.packer :refer [fit-words-memoized]]
[clojure.string :as string]))

;; ----------------------------------------------------------------------------
Expand Down Expand Up @@ -29,6 +29,7 @@

(defn normalize-sort-scale [keywords]
(->> keywords
(take 200)
(normalize)
(sort-by :weight)
(reverse)
Expand All @@ -51,5 +52,5 @@
(fn [keywords]
(let [w (width-from-window @window-size)]
[:div.word-cloud
[fit-words w 500
[fit-words-memoized w 500
(normalize-sort-scale keywords)]]))))
14 changes: 9 additions & 5 deletions src/cljs/ulysses/handlers.cljs
Expand Up @@ -4,6 +4,7 @@
[ulysses.config :as config]
[ulysses.db :as db]
[ulysses.lib.moment :as m]
[ulysses.lib.packer :refer [fit-words-memoized]]
[ulysses.components.misc :refer [inspector]]
[ulysses.utils :refer [document-size
window-size
Expand Down Expand Up @@ -202,11 +203,14 @@
(fn [faculties]
(merge
faculties
(-> response
:faculty
clean-incoming-faculty
list
by-id)))))))
(let [faculty (-> response
:faculty
clean-incoming-faculty)]
; memoize word cloud keyword sizes
(doall (fit-words-memoized (:keywords faculty)))
(-> faculty
list
by-id))))))))

;; ----------------------------------------------------------------------------
;; user data
Expand Down
84 changes: 44 additions & 40 deletions src/cljs/ulysses/lib/packer.cljs
Expand Up @@ -7,56 +7,60 @@
[w h blocks]
(let [p (new Packer w h)]
(->> blocks
(sort-by :h)
(reverse)
(clj->js)
(.fit p)
(js->clj)
(keywordize-keys)
(map
(fn [block]
(update block :fit dissoc
:used :right :down :w :h))))))
(update block "fit" dissoc
"used" "right" "down" "w" "h"))))))

(defn text-dimensions
[text options]
[text font-size]
(println text)
(-> text
(textDimensions (clj->js options))
(textDimensions font-size)
(js->clj)
(keywordize-keys)))

(def text-dimensions-memoized
(memoize text-dimensions))

(defn fit-words
[w h words]
(let [style-base {:fontFamily "Source Sans Pro" :fontWeight "100"}]
[:div
{:style {:position :relative :width w :height h}}
(->> words
(map
(fn [{:keys [keyword weight]}]
(let [style (assoc style-base :fontSize (-> weight (* 80) (str "px")))]
(-> keyword
(text-dimensions style)
; padding
(update :w
(partial + 8))
; margin
(update :w
(partial + 4))
(update :h
(partial + 4))
; style
(assoc :style style)
(assoc :weight weight)))))
(fit w h)
(map-subels
(fn [{:keys [w h fit name style weight]}]
[:a.keyword
{:style
(assoc style
:width (- w 4)
:height (- h 4)
:left (:x fit)
:top (:y fit))
:class
(str "weight-" (Math/round (* 100 weight)))}
name])))]))
[:div
{:style {:position :relative :width w :height h}}
(->> words
(map
(fn [{:keys [keyword weight]}]
(let [font-size (-> weight (* 80) (str "px"))]
(-> keyword
(text-dimensions-memoized font-size)
; padding
(update :w
(partial + 8))
; margin
(update :w
(partial + 4))
(update :h
(partial + 4))
; style
(assoc :font-size font-size)
(assoc :weight weight)))))
(doall)
(fit w h)
(map-subels
(fn [{:strs [w h fit name font-size weight]}]
[:a.keyword
{:style
{:fontSize font-size
:width (- w 4)
:height (- h 4)
:left (get fit "x")
:top (get fit "y")}
:class
(str "weight-" (Math/round (* 100 weight)))}
name])))])

(def fit-words-memoized
(memoize fit-words))
1 change: 1 addition & 0 deletions src/cljs/ulysses/utils.cljs
Expand Up @@ -43,6 +43,7 @@
"print x, then return it. useful for
debugging inside threading macros or map"
[x]
(println (new js/Date))
(cljs.pprint/pprint x)
x)

Expand Down
74 changes: 31 additions & 43 deletions src/js/extra.js
Expand Up @@ -87,64 +87,52 @@ ulysses.extra = (function() {

/**
* Get the dimensions of a piece of text
* textDimensions('hello world!',
* { fontFamily: 'Arial', fontSize: '16px', fontWeight: 'normal'})
* ex. textDimensions('hello world!', '16px');
*/
var textDimensions = (function() {
var createDummyElement = function(name, options) {
var element = document.createElement('div'),
nameNode = document.createTextNode(name);
var element = null;
var nameNode = null;

element.appendChild(nameNode);
var createDummyElement = function(name, fontSize) {
if (!element) {
element = document.createElement('div');
nameNode = document.createTextNode(name);

element.style.fontFamily = options.fontFamily;
element.style.fontSize = options.fontSize;
element.style.fontWeight = options.fontWeight;
element.style.position = 'absolute';
element.style.visibility = 'hidden';
element.style.left = '-999px';
element.style.top = '-999px';
element.style.width = 'auto';
element.style.height = 'auto';
element.appendChild(nameNode);

element.style.lineHeight = '1.3';
element.style.fontFamily = 'Source Sans Pro';
element.style.fontWeight = '100';
element.style.position = 'absolute';
element.style.visibility = 'hidden';
element.style.left = '-999px';
element.style.top = '-999px';
element.style.width = 'auto';
element.style.height = 'auto';

document.body.appendChild(element);
} else {
var newNameNode = document.createTextNode(name);
element.replaceChild(newNameNode, nameNode);
nameNode = newNameNode;
}

document.body.appendChild(element);
element.style.fontSize = fontSize;

return element;
};

var destroyElement = function(element) {
element.parentNode.removeChild(element);
}

var cache = {};

return function(name, options) {
var cacheKey = JSON.stringify({ name: name, options: options });

if (cache[cacheKey]) {
return cache[cacheKey];
}

// prepare options
options = options || {};
options.fontFamily = options.fontFamily || 'Times';
options.fontSize = options.fontSize || '16px';
options.fontWeight = options.fontWeight || 'normal';
var getDomTextDimensions = function(name, fontSize) {
var element = createDummyElement(name, fontSize);

var element = createDummyElement(name, options);

var result = {
return {
w: element.offsetWidth,
h: element.offsetHeight,
name: name
};

destroyElement(element);

cache[cacheKey] = result;

return result;
};

return getDomTextDimensions;
})();

return {
Expand Down
3 changes: 3 additions & 0 deletions src/sass/components/_word-cloud.scss
Expand Up @@ -7,6 +7,9 @@
background: $black;
color: $white;
display: block;
font-family: 'Source Sans Pro', sans-serif;
font-weight: 100;
line-height: 1.3;
overflow: hidden;
position: absolute;
text-align: center;
Expand Down

0 comments on commit 796c20d

Please sign in to comment.