The following is a little SparkLine generator written in the Clojure language.
There’s an example in the code that covers the usage.
Please post a comment if you find it useful, or have suggestions.
I have since updated this program. It should be pixel-perfect, and much more resistant to bad data.
Thanks Rich, for the comment below. I have updated the main function to use destructuring bind on the function parameters.
; Sparkline Generator
; Copyright (c) Jonathan A Watmough. All Rights Reserved.
;
; The use and distribution terms for this software are covered by the
; Common Public License 1.0 (http://opensource.org/licenses/cpl1.0.txt).
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license. Do not remove this notice.
(defn
#^{ :doc "Scale a list of numeric 'values' to a max height of 'height'."
:private true}
scale-values ([values height]
(let [highest (apply max (filter identity values))]
(map #(if % (dec (- height (/ (* % (- height 3)) highest))) nil) values))))
(defn
#^{:doc "Make a Win-style java.awt.image.BufferedImage of 'width' x 'height'."}
make-buffered-image ([width height]
(let [image-type (. java.awt.image.BufferedImage TYPE_3BYTE_BGR)]
(new java.awt.image.BufferedImage width height image-type))))
(defn
#^{:doc "Create pixel-accurate sparkline bitmap graphic from {:width :height :values :marker}. :marker=-1 for last entry."}
make-sparkline ([{ w :width h :height v :values m :marker }]
(let [v (scale-values v h)
bitmap (make-buffered-image w h)
g (. bitmap (getGraphics))
m (if (and m (< m 0)) (+ m (count v)) m)
step (/ (- w 3) (- (count v) 1))]
(do (doto g (setColor (. java.awt.Color white))
(fillRect 0 0 w h)
(setColor (. java.awt.Color gray)))
(dotimes idx (count v)
(let [x-pos (inc (* idx step))
prev-val (nth v idx)
this-val (nth v (inc idx))]
(when (and prev-val this-val)
(. g (drawLine x-pos (dec prev-val) (+ x-pos step) (dec this-val))))))
; Draw marker if present and return the sparkline bitmap
(when (and m (nth v m))
(let [bx (* m step)
by (- (nth v m) 2)]
(doto g (setColor (. java.awt.Color red))
(fillOval bx by 2 2))))
(. g (dispose))
bitmap))))
(defn
#^{:doc "Test code for sparklines graphic generator."}
test-sparklines []
(let [spark (make-sparkline {:width 100 :height 30
:values [1 2 10 8 2 5 8 12 14 3 4 15]
:marker -1
})
icon (new javax.swing.ImageIcon spark)]
(doto (new javax.swing.JFrame "Sparkline Test")
(add (new javax.swing.JLabel icon))
(pack)
(setVisible true))))
(test-sparklines)


You might want to try destructuring in let:
(let [{w :width h :height v :values} data] …
Nice. I like the Lispishness of Clojure. It might be something I’d consider if I had to write code running on the JVM.
Nice – I’ve only seen a few positive references to Clojure, and it looks fascinating. It’s interesting to see the way you mix Lisp and Java together – even if the class names look a bit scary.
You might be interested to see the sparkline generator I did. I only saw your version after I did mine, so I may be able to improve my code after seeing yours.
Will have to look at Clojure soon.
Cool. Good to see some well-written example code out there to learn from.
One thing I noticed is that it was written before the Java interop macros were implemented. You could shorted probably every s-exp containing Java. E.g. (. bitmap (getGraphics)) –> (.getGraphics bitmap)
Whether it’s worthwhile is up to you.
Hi. Thanks for writing this sample code. I ran into two problems running sparklines.clj as written (against clojure-20090320).
1. Calls against Java objects within (doto ..) needed to be .method.
(setColor …) -> (.setColor …)
Changed: setColor, fillRect, fillOval, add, pack, setVisible
2. dotimes takes a vector.
(dotimes idx (count v) …)
becomes
(dotimes [idx (dec (count v))] …)
The (dec …) was necessary to avoid an out-of-bounds index exception from:
this-val (nth v (inc idx))
on the last iteration.
Diff file:
—————————————-
30,33c30,33
< (do (doto g (setColor (. java.awt.Color white))
< (fillRect 0 0 w h)
< (setColor (. java.awt.Color gray)))
(do (doto g (.setColor (. java.awt.Color white))
> (.fillRect 0 0 w h)
> (.setColor (. java.awt.Color gray)))
> (dotimes [idx (dec (count v))]
43,44c43,44
< (doto g (setColor (. java.awt.Color red))
(doto g (.setColor (. java.awt.Color red))
> (.fillOval bx by 2 2))))
57,59c57,59
< (add (new javax.swing.JLabel icon))
< (pack)
(.add (new javax.swing.JLabel icon))
> (.pack)
> (.setVisible true))))