GithubHelp home page GithubHelp logo

dorothy's Introduction

dorothy Travis CI status

Hiccup-style generation of Graphviz graphs in Clojure and ClojureScript.

Dorothy is extremely alpha and subject to radical change. Release Notes Here

Usage

Dorothy assumes you have an understanding of Graphviz and DOT. The text below describes the mechanics of Dorothy's DSL, but you'll need to refer to the Graphviz documentation for specifics on node shapes, valid attributes, etc.

The Graphviz dot tool executable must be on the system path to render

Dorothy is on Clojars. In Leiningen:

[dorothy "x.y.z"]

A graph consists of a vector of statements. The following sections describe the format for all the types of statements. If you're bored, skip ahead to the "Defining Graphs" section below.

Node Statement

A node statement defines a node in the graph. It can take two forms:

node-id

[node-id]

[node-id { attr map }]

where node-id is a string, number or keyword with optional trailing port and compass-point. Here are some node statement examples:

:node0          ; Define a node called "node0"

:node0:port0    ; Define a node called "node0" with port "port0"

:node0:port0:sw ; Similarly a node with southwest compass point

the node's attr map is a map of attributes for the node. For example,

[:start {:shape :Mdiamond}]
; => start [shape=Mdiamond];

Dorothy will correctly escape and quote node-ids as required by dot.

A node id can also be auto-generated with (gen-id object). For example,

[(gen-id some-object) {:label (.getText some-object)}]

It allows you to use arbitrary objects as nodes.

Edge Statement

An edge statement defines an edge in the graph. It is expressed as a vector with two or more node-ids followed optional attribute map:

[node-id0 node-id1 ... node-idN { attr map }]
; => "node-id0" -> "node-id1" -> ... -> "node-idN" [attrs ...];

In addition to node ids, an edge statement may also contain subgraphs:

[:start (subgraph [... subgraph statements ...])]

For readability, :> delimiters may be optionally included in an edge statement:

[:start :> :middle :> :end]

Graph Attribute Statement

A graph attribute statement sets graph-wide attributes. It is expressed as a single map:

{:label "process #1", :style :filled, :color :lightgrey}
; => graph [label="process #1",style=filled,color=lightgrey];

alternatively, this can be expressed with the (graph-attrs) function like this:

(graph-attrs {:label "process #1", :style :filled, :color :lightgrey})
; => graph [label="process #1",style=filled,color=lightgrey];

Node and Edge Attribute Statement

A node attribute or edge attribute statement sets node or edge attributes respectively for all nodes and edge statements that follow. It is expressed with (node-attrs) and (edge-attrs) statements:

(node-attrs {:style :filled, :color :white})
; => node [style=filled,color=white];

or:

(edge-attrs {:color :black})
; => edge [color=black];

Defining Graphs

As mentioned above, a graph consists of a series of statements. These statements are passed to the graph, digraph, or subgraph functions. Each takes an optional set of attributes followed by a vector of statements:

; From http://www.graphviz.org/content/cluster
(digraph [
  (subgraph :cluster_0 [
    {:style :filled, :color :lightgrey, :label "process #1"}
    (node-attrs {:style :filled, :color :white})

    [:a0 :> :a1 :> :a2 :> :a3]])

  (subgraph :cluster_1 [
    {:color :blue, :label "process #2"}
    (node-attrs {:style :filled})

    [:b0 :> :b1 :> :b2 :> :b3]])

  [:start :a0]
  [:start :b0]
  [:a1    :b3]
  [:b2    :a3]
  [:a3    :a0]
  [:a3    :end]
  [:b3    :end]

  [:start {:shape :Mdiamond}]
  [:end   {:shape :Msquare}]])

Similarly for (graph) (undirected graph) and (subgraph). A second form of these functions takes an initial option map, or a string or keyword id for the graph:

(graph :graph-id ...)
; => graph "graph-id" { ... }

(digraph { :id :G :strict? true } ...)
; => strict graph G { ... }

Generate Graphviz dot format

Given a graph built with the functions described above, use the (dot) function to generate Graphviz DOT output.

(require '[dorothy.core :as dot])
(def g (dot/graph [ ... ]))
(dot/dot g)
"graph { ... }"

Rendering images (ClojureScript)

Dorothy currently doesn't include any facilities for rendering dot-format output to images. However, you can pull in viz.cljc or viz.js, both of which will allow you to produce png, svg, and other image formats from your dorothy-generated dot-formatted graph content.

Wanted: pull requests to implement node equivalents to the rendering functions available for Clojure/JVM in the dorothy.jvm namespace. link to github issue here

Render images via graphviz (Clojure/JVM)

Once you have DOT language output, you can render it as an image using the (render) function:

(require '[dorothy.jvm :refer (render save! show!)])

; This produces a png as an array of bytes
(render graph {:format :png})

; This produces an SVG string
(render graph {:format :svg})

; A one-liner with a very simple 4 node digraph.
(-> (dot/digraph [ [:a :b :c] [:b :d] ])
    dot/dot
    (render {:format :svg}))

The dot tool executable must be on the system path

other formats include :pdf, :gif, etc. The result will be either a java byte array, or String depending on whether the format is binary or not. (render) returns a string or a byte array depending on whether the output format is binary or not.

Alternatively, use the (save!) function to write to a file or output stream.

; A one-liner with a very simple 4 node digraph
(-> (dot/digraph [ [:a :b :c] [:b :d] ])
    dot/dot
    (save! "out.png" {:format :png}))

Finally, for simple tests, use the (show!) function to view the result in a simple Swing viewer:

; This opens a simple Swing viewer with the graph
(show! graph)

; A one-liner with a very simple 4 node digraph
(-> (dot/digraph [ [:a :b :c] [:b :d] ])
    dot/dot
    show!)

which shows:

License

Copyright (C) 2011-2017 Dave Ray and contributors

Distributed under the Eclipse Public License, the same as Clojure.

dorothy's People

Contributors

brandonbloom avatar cemerick avatar daveray avatar enaeher avatar engelberg avatar jafingerhut avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

dorothy's Issues

Release 0.0.6?

Hey,

The issue fixed in PR #6 prevents deploying of apps with a dependency on dorothy to a headless environment. I'm using reduce-fsm, which uses dorothy to visualize FSMs, and so I'm getting this issue when deploying my app.

Any chance you could push out a release with that fix? I'll build myself a snapshot meanwhile.

Thanks!

No tag in repo for release 0.0.7

There is no tag in the repo for which commit is 0.0.7, but according to the pom.xml in the jar it is

7addbb976a275b3a287e2ed04d891d18507f8aea

Since some functions moved to dorothy.jvm this breaks ubergraph 0.8.2 (issue), and I could not easily see what had changed in dorothy between the versions, hence this issue.

.cljc conversion for ClojureScript use

Looks like this will be pretty straightforward.

My preference would be to move the rendering bits into a new dorothy.jvm namespace. That's obviously a breaking change, but it would leave room for node- and browser-specific namespaces for rendering in those environments later. If you'd prefer not to do that, let me know and I'll keep the jvm rendering stuff where it is.

RuntimeException on ER example

When I run the example er.clj in a lein repl I get the following output

java.lang.RuntimeException: java.lang.RuntimeException: java.lang.RuntimeException: java.lang.RuntimeException: Invalid id: class java.lang.Double (NO_SOURCE_FILE:0)

When I run the swing.clj example everything is fine

I am relatively unfamiliar with clojure and lein though so this could be a rookie mistake on my end.

Unescape when creating SVG files

Thanks for making dorothy. It is really easy to use. I am currently using it to generate SVG diagrams. In doing so I have trying to embed links in the diagrams. Graphviz of course supports links on nodes and edges, but I need to generate links on for instance a part of a label. To that end I have tried to embed raw svg strings in the label text, but dorothy (graphviz?) escape these. Is there a way to circumvent this behaviour or an alternative way of accomplishing what I am trying to do?

digraph sometimes doesn't render parallel edges, depending on order.

=> (-> [[1 3 {:color :blue, :dir :none}]
        [1 3 {:color :red, :dir :none}]
        [1 2 {:color :green, :dir :none}]]
     digraph
     dot
     show!)

This renders the graph correctly, but simply swap two of the edges and it no longer works properly:

=> (-> [[1 3 {:color :blue, :dir :none}]
        [1 2 {:color :green, :dir :none}]
        [1 3 {:color :red, :dir :none}]]
     digraph
     dot
     show!)

dorothy produces dot files that at least several recent Graphviz versions cannot parse for particular attribute values

In particular, when a node or edge label is a string value like "node" or "edge", and perhaps other strings that seem to be some kind of reserved keywords in the dot language, as of Dorothy version 0.7 it generates dot files where those attribute values do not have double quotes surrounding them, and several versions of Graphviz's dot command give an error when trying to read such a file.

Here are fairly short steps to reproduce:

(require '[dorothy.core :as d])

(def dorothy-digraph-args
  '({}
    ({:layout :dot}
     [:a {}]
     [:b {}]
     [:a :b {:label "node"}])))

(def x1 (d/digraph dorothy-digraph-args))
(def x2 (d/dot x1))
(spit "simple.dot" x2)
$ cat simple.dot
digraph {
graph [];
graph [layout=dot];
a;
b;
a -> b [label=node];
} 

Then in a terminal on an Ubuntu 18.04 Linux system with this version of dot installed, dot gives an error while attempting to parse that file:

$ dot -V
dot - graphviz version 2.40.1 (20161225.0304)
$ dot -Tpdf simple.dot > simple.pdf
Error: simple.dot: syntax error in line 6 near 'node'

Similarly on an Ubuntu 20.04 Linux system with this version of dot installed:

$ dot -V
dot - graphviz version 2.43.0 (0)
$ dot -Tpdf simple.dot > simple.pdf
Error: simple.dot: syntax error in line 6 near 'node'

Similarly on a macOS 10.14.6 system with this version of dot installed:

$ dot -V
dot - graphviz version 2.44.1 (20200629.0846)
$ dot -Tpdf simple.dot > simple.pdf
Error: simple.dot: syntax error in line 6 near 'node'

On all three system + Graphviz versions mentioned above, editing the file simple.dot to the following, adding double quotes around the values of all attributes, enabled dot to correctly parse the file and produce the desired drawing:

$ cat simple2.dot
digraph {
graph [];
graph [layout="dot"];
a;
b;
a -> b [label="node"];
} 

Rank syntax support

I was unable to generate the syntax for {rank = same; X; Y; Z} without using some dorothy internals.

Here's a small example of what I'd like to do: https://stackoverflow.com/a/44274606/1481316

This is also mentioned in the Dot User's Manual https://www.graphviz.org/pdf/dotguide.pdf on page 16.

Here's how I achieved the syntax, which as far as I can tell is called "Rank":

(defn rank
  [attrs statements]
  {:type ::rank
   :statements (map #'dot/to-ast statements)
   :attrs attrs})

(defmethod dot/dot* ::rank
  [this]
  (let [{:keys [attrs statements]} this]
    (str "{\n"
         (apply
           str
           (for [[k v] attrs]
             (str (#'dot/escape-id k) \= (#'dot/escape-id v) ";\n")))
         (apply str (interleave
                      (map dot/dot* statements)
                      (repeat ";\n")))
         "} ")))

It's a lot like a graph, but without a name, and with the ability to set inline attrs. I decided to front-load the attrs, but ordering might matter, so that might not be ideal.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.