Racket Graphviz Integration
(require graphviz) | package: racket-graphviz |
The composition is made possible through:
You can use graphviz diagrams as normal picts
You can use any Pict as node shape of graphviz diagrams
1 Basic concepts
This package helps with visualizing directed graphs, or digraphs for short. Each digraph consists of a set of vertexes and edges. For example the digraph in figure 1 consists of three vertexes and four edges.
Figure 1: An example digraph
There can be multiple edges between two vertexes, as shown in figure 2.
Figure 2: multiple edges between two nodes
Furthermore, a set of vertexes can be grouped in a subgraph, as show in figure 3.
Figure 3: Subgraphs
2 API
2.1 Defining Subgraphs
procedure
(make-digraph definitions #:ortho ortho) → digraph?
definitions : list? ortho : boolean?
- Vertex Definitions. A vertex can be defined using:
A string. The string will be used as the label.
A list whose first element is the label and rest of the list contains the attributes of the node.
A call to make-vertex.
- Edge Definitions. A edge can be defined using:
A string. Node names are separated by ->
A list like (’edge [node1 ...] #:attr1 val1 ...).
Subgraph Definitions. A subgraph can be defined using (’subgraph label definitions)
For example, figure 1 can be defined as the following, where vertexes and edges are defined using strings.
(make-digraph `("v0" "v1" "v2" "v0 -> v0" "v0 -> v1" "v1 -> v2" "v2 -> v0"))
figure 3 can be defined as the following:
(make-digraph `((subgraph "Coordinator" ("Parser -> Planner -> Executor")) (subgraph "Worker1" ("QueryProcessor1 -> DataPartition1")) (subgraph "Worker2" ("QueryProcessor2 -> DataPartition2")) "Executor -> QueryProcessor1" "QueryProcessor1 -> Executor" "Executor -> QueryProcessor2" "QueryProcessor2 -> Executor"))
procedure
(make-vertex label #:shape shape) → vertex?
label : string? shape : (or/c pict? string?)
2.2 Conversion to Pict
procedure
(digraph->pict digraph) → pict?
digraph : digraph?
(dot->pict "digraph { a -> b -> c; }")
Figure 4: @dot->pict example
2.3 Structs
struct
objects : list? ortho : boolean?
struct
name : string? label : string? shape : (or/c pict? string?) attrs : hash?
struct
nodes : list? attrs : hash?
struct
label : string? objects : list? attrs : hash?
3 Examples
3.1 Android Activity Lifecycle
(define (shadowed-box w h color) (inset (shadow (filled-rounded-rectangle w h 5 #:color color #:border-color "black") 15 -3 3) 10)) (define start-pict (shadowed-box 120 40 "LightSkyBlue")) (define node-pict (shadowed-box 160 40 "Gainsboro")) (define running-pict (shadowed-box 150 40 "Aquamarine")) (define terminal-pict (shadowed-box 150 40 "Salmon")) (define d (make-digraph `(("Start" #:label "Activity Starts" #:shape ,start-pict) ("onCreate" #:label "onCreate()" #:shape ,node-pict) ("onStart" #:label "onStart()" #:shape ,node-pict) ("onResume" #:label "onResume()" #:shape ,node-pict) ("Running" #:label "Activity Running" #:shape ,running-pict) ("onPause" #:label "onPause()" #:shape ,node-pict) ("onStop" #:label "onStop()" #:shape ,node-pict) ("onDestroy" #:label "onDestroy()" #:shape ,terminal-pict) ("onRestart" #:label "onRestart()" #:shape ,node-pict) ("killed" #:label "Process is Killed" #:shape ,terminal-pict) (edge ("Start" "onCreate" "onStart" "onResume" "Running") #:weight "7") (edge ("Running" "onPause") #:label "Another activity activates" #:weight "7") (edge ("onPause" "onStop") #:label "Activity is no longer visible" #:weight "7") (edge ("onStop" "onDestroy") #:weight "7") (edge ("onPause" "onResume")) (edge ("onStop" "onRestart") #:label "Activity comes to foreground") (edge ("onRestart" "onStart")) (edge ("onStop" "killed")) (edge ("killed" "onCreate")) (same-rank "killed" "Running" "onRestart")) #:ortho #t)) (scale (inset (digraph->pict d) 10) 0.8)
Figure 5: Android Activity Lifecycle
3.2 Turnstile State Machine
(make-digraph `(("Locked" #:shape "circle" #:width "1.2") ("Unlocked" #:shape "circle" #:width "1.2") (edge ("Locked:n" "Locked:n") #:label "Push") (edge ("Locked" "Unlocked") #:label "Coin") (edge ("Unlocked" "Locked") #:label "Push") (edge ("Unlocked:n" "Unlocked:n") #:label "Coin") (same-rank "Locked" "Unlocked")))