diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d8531bb..25c8289 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,5 +1,9 @@ This library is a wrapper around [kumo](https://github.com/kennycason/kumo) . You can look into the source for things to port. Fork and make a PR. +## API guidelines + +* API should be compatible with [kumo](https://github.com/kennycason/kumo) and please refer to the repo for implementing new APIs. + ## Code guidelines * Please use [aligned maps](https://github.com/bbatsov/clojure-style-guide#vertically-align-let-and-map) . In emacs select the map and hit `C-c SPC`. Refer : https://github.com/clojure-emacs/clojure-mode#vertical-alignment diff --git a/README.md b/README.md index f9617f0..de4d547 100644 --- a/README.md +++ b/README.md @@ -6,18 +6,69 @@ A simple clojure wrapper around kumo to generate wordcloud Given a map of element and the frequency the following image is generated. More examples/examples.clj. +### Circle + ```clojure -(let [frequency-map (zipmap (range 100 130) (range 300))] - (word-cloud frequency-map - {:background {:type :circle :size 300} - :filename "sample.png" :font-y 100 :padding 10})) +(ns examples + (:require '[clj-wordcloud.core :refer :all])) + +(let [frequency-map (zipmap (random-words 100) (shuffle (range 300))) + word-cloud (word-cloud frequency-map + {:dimension {:width 600 + :height 600} + :background {:type :circle + :size 300 + :color "0x000000"} + :font {:type "Calibre" + :weight :plain + :scale-type :linear + :x-scale 20 + :y-scale 20 + :padding 5}})] + (write-to-file word-cloud "example_circle.png")) ``` -![Sample](/examples/sample.png) +![Sample](/examples/example_circle.png) + +### Bitmaps + +```clojure +(ns examples + (:require '[clj-wordcloud.core :refer :all])) + +(let [frequency-map (zipmap (range 100 150) (shuffle (range 300))) + word-cloud (word-cloud frequency-map + {:dimension {:width 600 + :height 600} + :background {:type :pixel + :size 300 + :color "0x000000" + :bitmap "examples/backgrounds/haskell_1.bmp"} + :font {:type "Calibre" + :weight :plain + :scale-type :linear + :x-scale 20 + :y-scale 20 + :padding 5}})] + (write-to-file word-cloud "example_haskell.png")) +``` + +![Sample](/examples/example_haskell.png) + +## TODO + +* API parity with Kumo +* clojure.spec for docs and validation +* Cool examples +* Better tests + +## Contributing + +Contributions are welcome. Please refer to CONTRIBUTING.md. ## Stability -This library is still in early development phase and the API is subject to change. +This library is still in early development phase and the API is subject to change. API design comments are welcome. ## Thanks diff --git a/examples/backgrounds/haskell_1.bmp b/examples/backgrounds/haskell_1.bmp new file mode 100644 index 0000000..0b8a63a Binary files /dev/null and b/examples/backgrounds/haskell_1.bmp differ diff --git a/examples/backgrounds/haskell_2.bmp b/examples/backgrounds/haskell_2.bmp new file mode 100644 index 0000000..075e18a Binary files /dev/null and b/examples/backgrounds/haskell_2.bmp differ diff --git a/examples/example_circle.png b/examples/example_circle.png new file mode 100644 index 0000000..eabc3fa Binary files /dev/null and b/examples/example_circle.png differ diff --git a/examples/example_haskell.png b/examples/example_haskell.png new file mode 100644 index 0000000..dc82100 Binary files /dev/null and b/examples/example_haskell.png differ diff --git a/examples/examples.clj b/examples/examples.clj index 3bce873..c2cebc1 100644 --- a/examples/examples.clj +++ b/examples/examples.clj @@ -1,7 +1,47 @@ (ns examples (:require '[clj-wordcloud.core :refer :all])) -(let [frequency-map (zipmap (range 100 130) (range 300))] - (word-cloud frequency-map - {:background {:type :circle :size 300} - :filename "sample.png" :font-y 100 :padding 10})) + +(defn random-words + [n] + (->> "/usr/share/dict/words" + slurp + clojure.string/lower-case + clojure.string/split-lines + shuffle + (take n))) + + +(let [frequency-map (zipmap (random-words 100) (shuffle (range 300))) + word-cloud (word-cloud frequency-map + {:dimension {:width 600 + :height 600} + :background {:type :circle + :size 300 + :color "0x000000"} + :font {:type "Calibre" + :weight :plain + :scale-type :linear + :x-scale 20 + :y-scale 20 + :padding 5}})] + (write-to-file word-cloud "example_circle.png")) + + +(let [frequency-map (zipmap (range 100 150) (shuffle (range 300))) + word-cloud (word-cloud frequency-map + {:dimension {:width 600 + :height 600} + :background {:type :pixel + :size 300 + :color "0x000000" + :bitmap "examples/backgrounds/haskell_1.bmp"} + :font {:type "Calibre" + :weight :plain + :scale-type :linear + :x-scale 20 + :y-scale 20 + :padding 5 + :colors ["0x00FF00" "0x0000FF" "0xFFAFFF" + "0xFFEEFF" "0xEEEEEE"]}})] + (write-to-file word-cloud "example_haskell.png")) diff --git a/project.clj b/project.clj index adfcbd9..9fe5f47 100644 --- a/project.clj +++ b/project.clj @@ -1,4 +1,4 @@ -(defproject xtreak/clj-wordcloud "0.1.0-SNAPSHOT" +(defproject xtreak/clj-wordcloud "0.0.1-SNAPSHOT" :description "A simple clojure wrapper around kumo for wordcloud generation" :url "http://github.com/tirkarthi/clj-wordcloud" :license {:name "MIT public license" diff --git a/src/clj_wordcloud/core.clj b/src/clj_wordcloud/core.clj index 9063669..01a038d 100644 --- a/src/clj_wordcloud/core.clj +++ b/src/clj_wordcloud/core.clj @@ -1,21 +1,29 @@ (ns clj-wordcloud.core - (:import (com.kennycason.kumo WordCloud WordFrequency) - (java.awt Dimension) - (com.kennycason.kumo WordCloud WordFrequency CollisionMode) - (com.kennycason.kumo.bg CircleBackground RectangleBackground) + (:import (java.awt Dimension Color) + (java.awt.image BufferedImage) + (javax.imageio ImageIO) + (com.kennycason.kumo WordCloud WordFrequency LayeredWordCloud CollisionMode) + (com.kennycason.kumo.bg CircleBackground RectangleBackground PixelBoundryBackground + PixelBoundryBackground) + (com.kennycason.kumo.palette ColorPalette) + (com.kennycason.kumo.font KumoFont FontWeight) (com.kennycason.kumo.font.scale LinearFontScalar) (com.kennycason.kumo.nlp FrequencyAnalyzer))) (defn- background-object [options dimension] - (let [type (get-in options [:background :type] :circle) - size (get-in options [:background :size] 20)] + (let [type (get-in options [:background :type] :circle) + size (get-in options [:background :size] 20) + bitmap (get-in options [:background :bitmap])] (case type :circle (CircleBackground. size) :rectangle - (RectangleBackground. dimension)))) + (RectangleBackground. dimension) + :pixel + (PixelBoundryBackground. (clojure.java.io/input-stream bitmap)) + (CircleBackground. size)))) (defn- build-word-frequency @@ -25,16 +33,79 @@ (.add %1 (WordFrequency. (str word) count)))))) +(defn- build-font-scalar + [options] + (let [scale-type (get-in options [:font :scale-type] :linear) + x-scale (get-in options [:font :x-scale] 10) + y-scale (get-in options [:font :y-scale] 10)] + (case scale-type + :linear + (LinearFontScalar. x-scale y-scale)))) + + +(defn- build-font + [options] + (let [font-map {:plain FontWeight/PLAIN, :bold FontWeight/BOLD, :italic FontWeight/ITALIC} + font-type (get-in options [:font :type] "Arial") + font-weight (get-in options [:font :weight] :plain)] + (KumoFont. font-type (get font-map font-weight FontWeight/PLAIN)))) + + +(defn- build-dimension + [options] + (let [width (get-in options [:dimension :width] 100) + height (get-in options [:dimension :height] 100)] + (Dimension. width height))) + + +(defn- build-color-palette + [options] + (let [default-colors ["0xFFFF00" "0x008000" "0x0000FF"] + colors (->> default-colors + (concat (get-in options [:font :colors] [])) + (take 5)) + color-palette (map #(Color/decode %1) colors)] + (ColorPalette. color-palette))) + + +(defn write-to-file + "Writes the word cloud object as png image to the given location. + Supply file name with extension png like example.png" + + [word-cloud filename] + (.writeToFile word-cloud filename)) + + (defn word-cloud + " + Takes a map of string and the score along with options to return a wordcloud object + Options spec as below : + + {:dimension {:width 600 ; width of the image + :height 600} ; height of the image + :background {:type :circle ; type. Takes :circle, :pixel (bitmaps), :rectangle + :size 300 ; size of the circle + :color \"0x000000\"} ; Background color as hex + :font {:type \"Calibre\" ; Font type + :weight :plain ; Font weight. Takes :plain, :bold, :italic + :scale-type :linear ; linear for now + :x-scale 20 ; x scale of font + :y-scale 20 ; y scale of font + :padding 5}} ; padding between entries + " [frequency-map options] (let [word-frequencies (build-word-frequency frequency-map) - dimension (Dimension. (:width options 600) (:height options 600)) + dimension (build-dimension options) word-cloud (WordCloud. dimension CollisionMode/PIXEL_PERFECT) background (background-object options dimension) - font (LinearFontScalar. (:font-x options 10) (:font-y options 40))] + font-scalar (build-font-scalar options) + kumo-font (build-font options) + color-palette (build-color-palette options)] (doto word-cloud - (.setPadding (:padding options 10)) + (.setPadding (get-in options [:font :padding] 10)) + (.setBackgroundColor (Color/decode (get-in options [:background :color] "0x000000"))) + (.setColorPalette color-palette) (.setBackground background) - (.setFontScalar font) - (.build word-frequencies) - (.writeToFile (:filename options "test.png"))))) + (.setFontScalar font-scalar) + (.setKumoFont kumo-font) + (.build word-frequencies)))) diff --git a/test/clj_wordcloud/core_test.clj b/test/clj_wordcloud/core_test.clj index bd28a69..bbadbb9 100644 --- a/test/clj_wordcloud/core_test.clj +++ b/test/clj_wordcloud/core_test.clj @@ -9,9 +9,22 @@ (let [image (javax.imageio.ImageIO/read r)] [(.getWidth image) (.getHeight image)]))) + (deftest image-dimensions-test (testing "Test width and height of the generated image" - (let [word-cloud (word-cloud (zipmap (range 100 130) (range 300)) - {:background {:type :circle :size 300} - :filename "sample.png" :font-y 100 :padding 10})] + (let [freq-map (zipmap (range 100 130) (range 300)) + word-cloud (word-cloud freq-map + {:dimension {:width 600 + :height 600} + :background {:type :circle + :size 300 + :color "0x000000"} + :font {:type "Comic Sans MS" + :scale-type :linear + :x-scale 20 + :y-scale 20 + :padding 5 + :colors ["0x00FF00" "0x0000FF" "0xFFAFFF" + "0xFFEEFF" "0xEEEEEE"]}})] + (write-to-file word-cloud "sample.png") (is (= (get-size "sample.png") [600 600])))))