Create Graphviz graphs in Clojure with dorothy

Ben Lovy - Feb 23 '19 - - Dev Community

Quick post today - learning this made me feel like I levelled up, so I thought I'd share.

I recently decided to remove the "self" from self-taught and go back to school for software development. To nobody's surprise at all, my first semester includes a Discrete Mathematics class, and this week we looked at Hasse diagrams, used to represent partially ordered sets. This is just the sort of diagram graphviz is designed for!

Being a C library, Graphviz has interfaces in just about any language you could hope for. Python is a good choice for quick one-offs like this, but it's also a natural fit for Clojure! Also to nobody's surprise at all, the community has created a library for defining Graphviz graphs using Clojure data structures, like what Hiccup does for HTML. It's called dorothy.

To follow along, you'll need to install leiningen and graphviz. Once both are installed, create a new project:



$ lein new app hasse


Enter fullscreen mode Exit fullscreen mode

Open up the hasse folder in your favorite text editor and find project.clj. We just need to add the dorothy dependency. Locate the :dependencies map and make it look like this:



  :dependencies [[org.clojure/clojure "1.9.0"]
                 [dorothy "0.0.7"]]


Enter fullscreen mode Exit fullscreen mode

Now run lein deps to pull in the jar and open up src/hasse/core.clj. Below the (ns) form, add the following require statements:



(ns hasse.core
  (:gen-class))
(require '[dorothy.core :as dot])
(require '[dorothy.jvm :refer (render save!)])]


Enter fullscreen mode Exit fullscreen mode

To create a graph, you just define your nodes and edges - and in this case, all we need to do is define edges. Graphviz will take care of everything else. Remove the body of -main and add a let binding to define our graph:



  (let [g (dot/graph [
    [:22 :2]
    [:8 :2]
    [:10 :5 :1]
    [:10 :2 :1]])])


Enter fullscreen mode Exit fullscreen mode

There is also a dot/digraph which will create directed edges. Each keyword is a node, and each list is an edge connecting two or more nodes. Each node can take an attribute map as well, I'm just using the defaults for everything here. Now that we've defined the graph, we use the (dot/dot) function to convert it to the Graphviz dot format. We can then use save! to save our result:



(-> g dot/dot (save! "out.png" {:format :png}))


Enter fullscreen mode Exit fullscreen mode

There is also a show! in dorothy.jvm which uses a simple Swing viewer - useful for testing. Your full snippet should look like this:



(ns hasse.core
  (:gen-class))
(require '[dorothy.core :as dot])
(require '[dorothy.jvm :refer (render save!)])

(defn -main
  [& args]
  (let [g (dot/graph [
    [:22 :2]
    [:8 :2]
    [:10 :5 :1]
    [:10 :2 :1]])]
    (-> g dot/dot (save! "out.png" {:format :png}))))


Enter fullscreen mode Exit fullscreen mode

Now run lein uberjar to compile the Clojure, execute java -jar target/uberjar/hasse-0.1.0-SNAPSHOT-standalone.jar, and marvel at the beauty:

graph

Nifty! That sure as heck is a Hasse diagram of the relation "divides" on the set {1,2,5,8,10,22}, I tell you hwat.

This is usable from ClojureScript as well, but without the rendering and saving functions - you'll need to rely on another library to get your dot format output to something visual.

Happy diagrammin'!

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .