0% found this document useful (0 votes)
361 views

Agile Visualization

Uploaded by

jslr
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
361 views

Agile Visualization

Uploaded by

jslr
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 235

Agile Visualization

Alexandre Bergel
ii

Copyright © 2016 by Alexandre Bergel.

The contents of this book are protected under Creative Commons Attribution-ShareAlike 3.0
Unported license.
You are free:

to Share — to copy, distribute and transmit the work


to Remix — to adapt the work

Under the following conditions:

Attribution. You must attribute the work in the manner specified by the author or licensor (but
not in any way that suggests that they endorse you or your use of the work).
Share Alike. If you alter, transform, or build upon this work, you may distribute the resulting
work only under the same, similar or a compatible license.

• For any reuse or distribution, you must make clear to others the license terms of this
work. The best way to do this is with a link to this web page: creativecommons.org/licenses/
by-sa/3.0/
• Any of the above conditions can be waived if you get permission from the copyright
holder.
• Nothing in this license impairs or restricts the author’s moral rights.

Your fair dealing and other rights are in no way affected by the above. This
is a human-readable summary of the Legal Code (the full license):
creativecommons.org/licenses/by-sa/3.0/legalcode

Published by Lulu Press, Inc.


eBook ISBN 978-1-365-32585-4
First Edition, July, 2016 (v 1.01).
Contents

List of Figures ix

1 Introduction 1
1.1 Agile Visualization . . . . . . . . . . . . . . . . 2
1.2 The Roassal visualization engine . . . . . . . . . . . 2
1.3 Online and free version of this book . . . . . . . . . . 2
1.4 The Roassal community . . . . . . . . . . . . . . 2
1.5 Other visualization platforms . . . . . . . . . . . . 3
1.6 If you do not know Pharo . . . . . . . . . . . . . . 3
1.7 If you are already a Pharo/Smalltalk programmer . . . . . 4
1.8 Improving the book and Roassal . . . . . . . . . . . 4
1.9 Acknowledgements . . . . . . . . . . . . . . . . 5

I Roassal

2 Quick Start 9
2.1 Basic Installation . . . . . . . . . . . . . . . . . 9
2.2 Running Roassal . . . . . . . . . . . . . . . . . 10
2.3 First visualization . . . . . . . . . . . . . . . . . 10
2.4 Visualizing the file system . . . . . . . . . . . . . . 12
2.5 Geographical CSV data . . . . . . . . . . . . . . . 13
2.6 Seism activity over time . . . . . . . . . . . . . . 15
2.7 Charting . . . . . . . . . . . . . . . . . . . . 16
2.8 Timeline . . . . . . . . . . . . . . . . . . . . 20
2.9 Integration with OpenStreetMap . . . . . . . . . . . 21
iv Contents

2.10 More examples . . . . . . . . . . . . . . . . . . 24

3 Pharo In a Nutshell 25
3.1 Going over an example, Step-by-step . . . . . . . . . . 25
3.2 Pillars of Object-oriented programming . . . . . . . . . 27
3.3 Message sending . . . . . . . . . . . . . . . . . 28
3.4 Object creation . . . . . . . . . . . . . . . . . . 30
3.5 Class creation . . . . . . . . . . . . . . . . . . 31
3.6 Method creation . . . . . . . . . . . . . . . . . 31
3.7 Block closure . . . . . . . . . . . . . . . . . . 37
3.8 Control Structure . . . . . . . . . . . . . . . . . 38
3.9 Collection. . . . . . . . . . . . . . . . . . . . 38
3.10 Cascade . . . . . . . . . . . . . . . . . . . . 39
3.11 A bit of Meta-programming . . . . . . . . . . . . . 40
3.12 Summary and Further Reading . . . . . . . . . . . . 40

4 Painting with Trachel 41


4.1 Core of Trachel . . . . . . . . . . . . . . . . . 41
4.2 Using the Canvas . . . . . . . . . . . . . . . . . 41
4.3 Adding shapes . . . . . . . . . . . . . . . . . . 43
4.4 Fixed Shapes . . . . . . . . . . . . . . . . . . 44
4.5 Scaling and rotating . . . . . . . . . . . . . . . . 45
4.6 Callback and event handling . . . . . . . . . . . . . 45
4.7 Roassal generates Trachel shapes . . . . . . . . . . . 46

5 Visualizing with Roassal 47


5.1 View, Elements, Shapes and Interactions . . . . . . . . 47
5.2 Computing shapes dimensions . . . . . . . . . . . . 49
5.3 Shapes . . . . . . . . . . . . . . . . . . . . . 51
5.4 Element transformation. . . . . . . . . . . . . . . 58
5.5 Group of elements . . . . . . . . . . . . . . . . 58
5.6 Nesting elements . . . . . . . . . . . . . . . . . 58
5.7 Interaction to empower elements . . . . . . . . . . . 61
5.8 Composite shapes vs RTLabeled . . . . . . . . . . . 64
v

5.9 Normalization . . . . . . . . . . . . . . . . . . 66
5.10 Expressing constraints . . . . . . . . . . . . . . . 71
5.11 Shape and Edge builder . . . . . . . . . . . . . . 75
5.12 Dynamically update . . . . . . . . . . . . . . . . 77
5.13 Roassal visualization in a web browser . . . . . . . . . 80
5.14 Debugging a visualization. . . . . . . . . . . . . . 80

6 Coloring 81
6.1 Palettes . . . . . . . . . . . . . . . . . . . . 81
6.2 Multi-linear color normalizer. . . . . . . . . . . . . 82
6.3 Coloring objects . . . . . . . . . . . . . . . . . 86

7 Interactive Visualization 89
7.1 Draggable elements and view . . . . . . . . . . . . 89
7.2 Popup . . . . . . . . . . . . . . . . . . . . . 90
7.3 Fixed popup. . . . . . . . . . . . . . . . . . . 91
7.4 Graphical popup . . . . . . . . . . . . . . . . . 91
7.5 Highlighting elements . . . . . . . . . . . . . . . 94
7.6 Dynamically adding labels and edges . . . . . . . . . 96

8 Applying Layout 99
8.1 Element-based Layouts . . . . . . . . . . . . . . . 101
8.2 Edge-driven layouts . . . . . . . . . . . . . . . . 106
8.3 Layout builder . . . . . . . . . . . . . . . . . . 113
8.4 Creating custom layout . . . . . . . . . . . . . . . 116

9 Using the Camera 117


9.1 Operations . . . . . . . . . . . . . . . . . . . 117
9.2 Initialization. . . . . . . . . . . . . . . . . . . 117

II Builders

10 Domain-Specific Visualization Made Easy with Builders 121


10.1 Punch card example . . . . . . . . . . . . . . . . 122
10.2 Defining a builder with RTBuilder . . . . . . . . . . . 122
vi Contents

10.3 Specifying shapes with RTShapeBuilder. . . . . . . . . 124


10.4 Data Normalization . . . . . . . . . . . . . . . . 126
10.5 Specifying interaction using RTInteractionBuilder . . . . . 127

11 Visualizing Polymetric Graphs using Mondrian 129


11.1 A first visualization . . . . . . . . . . . . . . . . 129
11.2 Visualizing CSV data . . . . . . . . . . . . . . . 132
11.3 Mondrian Script Structure . . . . . . . . . . . . . . 133
11.4 Visualizing Software . . . . . . . . . . . . . . . . 134
11.5 Nesting . . . . . . . . . . . . . . . . . . . . 135
11.6 Visualizing File and Directories . . . . . . . . . . . . 138

12 Charting, Plotting and Curving using Grapher 141


12.1 Scatterplot . . . . . . . . . . . . . . . . . . . 141
12.2 Curve . . . . . . . . . . . . . . . . . . . . . 146
12.3 Labeled bar chart . . . . . . . . . . . . . . . . . 153
12.4 Interaction . . . . . . . . . . . . . . . . . . . 154
12.5 Decoration . . . . . . . . . . . . . . . . . . . 156
12.6 Translating the Y axis . . . . . . . . . . . . . . . 157
12.7 Date on the axis . . . . . . . . . . . . . . . . . 159
12.8 Several metrics per data point . . . . . . . . . . . . 160
12.9 Adding a legend . . . . . . . . . . . . . . . . . 161

13 Visualization Composition 163


13.1 Composing builders . . . . . . . . . . . . . . . . 163
13.2 Propagating events . . . . . . . . . . . . . . . . 165
13.3 Titled visualization . . . . . . . . . . . . . . . . 165
13.4 World population Example . . . . . . . . . . . . . 166

III Applications

14 Documenting with a Legend 171


14.1 A first example . . . . . . . . . . . . . . . . . . 171
14.2 Building a legend . . . . . . . . . . . . . . . . . 173
14.3 Documenting a polymetric shape . . . . . . . . . . . 173
vii

15 Expressing Epidemiological Models 175


15.1 Compartmental models of epidemiology . . . . . . . . 175
15.2 Visualizing Ebola epidemic outbreak from data . . . . . . 177
15.3 Loading Kendrick . . . . . . . . . . . . . . . . . 178
15.4 Measles model . . . . . . . . . . . . . . . . . . 179
15.5 Complex models in Kendrick . . . . . . . . . . . . 181
15.6 Example of multi-host model. . . . . . . . . . . . . 181
15.7 An example of Ebola spatial model with Kendrick . . . . . 183
15.8 Network Visualization of Epidemiological Models . . . . . 186

16 OpenStreetMap Integration 193


16.1 Moving the camera to particular locations . . . . . . . . 193
16.2 Decorating the map . . . . . . . . . . . . . . . . 196
16.3 Getting country location . . . . . . . . . . . . . . 198

17 Network Latency 201


17.1 Prelude . . . . . . . . . . . . . . . . . . . . 201
17.2 Network Latency Visual Analysis in a Nutshell . . . . . . 202
17.3 Loading NLVA . . . . . . . . . . . . . . . . . . 204
17.4 Visualizing latency in a proxy service. . . . . . . . . . 205
17.5 Visualizing your own connectivity . . . . . . . . . . . 205

18 Analyzing Software using Moose 211


18.1 Moose in a nutshell . . . . . . . . . . . . . . . . 212
18.2 Loading a software application . . . . . . . . . . . . 212
18.3 Browsing the source code . . . . . . . . . . . . . . 214
18.4 Visualizing the code distribution . . . . . . . . . . . 216
18.5 Visualizing cross-cutting concerns . . . . . . . . . . . 218
18.6 Dependencies using Moose Chef . . . . . . . . . . . 219
18.7 Reusing visualization . . . . . . . . . . . . . . . 220
18.8 More on that topic . . . . . . . . . . . . . . . . 221
List of Figures

2.1 Opening the playground. . . . . . . . . . . . . . . . . . . . . . 11


2.2 Connecting numbers. . . . . . . . . . . . . . . . . . . . . . . . . 11
2.3 Visualizing a file system. . . . . . . . . . . . . . . . . . . . . . . 13
2.4 Seismic activity the last 30 days. . . . . . . . . . . . . . . . . . . 14
2.5 Timeline of earthquakes. . . . . . . . . . . . . . . . . . . . . . . 15
2.6 Ebola fatalities. . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
2.7 Population per state in the USA. . . . . . . . . . . . . . . . . . 18
2.8 Multiple graphs. . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
2.9 Gantt chart. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
2.10 OpenStreetMap integration. . . . . . . . . . . . . . . . . . . . . 22
2.11 Charting on top of London. . . . . . . . . . . . . . . . . . . . . 23
2.12 Example browser. . . . . . . . . . . . . . . . . . . . . . . . . . . 24

3.1 Hello World. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26


3.2 Circles are lined up. . . . . . . . . . . . . . . . . . . . . . . . . . 26
3.3 The class Tweet. . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
3.4 The World menu and Spotter. . . . . . . . . . . . . . . . . . . . 33
3.5 Fibonacci of 10. . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
3.6 The method createFromURL:. . . . . . . . . . . . . . . . . . . . 36
3.7 Positive and Negative tweets. . . . . . . . . . . . . . . . . . . . 37

4.1 Instantiating canvas and a shape. . . . . . . . . . . . . . . . . . 42


4.2 Random size and colors. . . . . . . . . . . . . . . . . . . . . . . 43
4.3 Fixed shape. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
x List of Figures

5.1 Linking numbers. . . . . . . . . . . . . . . . . . . . . . . . . . . 48


5.2 Linking TSV elements. . . . . . . . . . . . . . . . . . . . . . . . 50
5.3 Example of labeling a box. . . . . . . . . . . . . . . . . . . . . . 53
5.4 Labeling a box of a particular size. . . . . . . . . . . . . . . . . 54
5.5 Giving a title to some boxes. . . . . . . . . . . . . . . . . . . . . 55
5.6 Giving a title to some boxes using RTLabeled. . . . . . . . . . 56
5.7 Giving a title to some boxes using RTLabeled and highlight. . 56
5.8 Composing shapes. . . . . . . . . . . . . . . . . . . . . . . . . . 57
5.9 Element rotation. . . . . . . . . . . . . . . . . . . . . . . . . . . 58
5.10 Nesting boxes into a larger box. . . . . . . . . . . . . . . . . . . 59
5.11 Using for:add: to nest elements. . . . . . . . . . . . . . . . . . . 60
5.12 Using for:add: and edges. . . . . . . . . . . . . . . . . . . . . . 61
5.13 Dragging connected elements. . . . . . . . . . . . . . . . . . . . 62
5.14 Composing shapes with a label. . . . . . . . . . . . . . . . . . . 65
5.15 Layout and RTLabeled. . . . . . . . . . . . . . . . . . . . . . . . 66
5.16 Normalizing element size. . . . . . . . . . . . . . . . . . . . . . 67
5.17 Normalizing element size and color. . . . . . . . . . . . . . . . 68
5.18 Normalizing element position. . . . . . . . . . . . . . . . . . . 69
5.19 Normalizing element height. . . . . . . . . . . . . . . . . . . . 69
5.20 Normalizing element height. . . . . . . . . . . . . . . . . . . . 70
5.21 Normalizing element height, width, and color. . . . . . . . . . 71
5.22 Use of alignment. . . . . . . . . . . . . . . . . . . . . . . . . . . 72
5.23 Group alignments. . . . . . . . . . . . . . . . . . . . . . . . . . 73
5.24 Positioning a group above another group. . . . . . . . . . . . . 75
5.25 Locating text above each box. . . . . . . . . . . . . . . . . . . . 76
5.26 Orthogonal lines obtained from a builder. . . . . . . . . . . . . 77
5.27 Updating edges upon mouse cursor movement. . . . . . . . . 78
5.28 Dynamically adding elements. . . . . . . . . . . . . . . . . . . 80

6.1 Example of a palette. . . . . . . . . . . . . . . . . . . . . . . . . 82


6.2 Simple coloring. . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
6.3 Simple coloring. . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
6.4 Classes and their size indicated using a color. . . . . . . . . . . 84
xi

6.5 Adding a legend. . . . . . . . . . . . . . . . . . . . . . . . . . . 86


6.6 Example of RTMultiLinearColorForIdentity. . . . . . . . . . . 87
6.7 Visualizing a poem. . . . . . . . . . . . . . . . . . . . . . . . . . 88

7.1 Simple example without interaction. . . . . . . . . . . . . . . . 90


7.2 Graphical popup. . . . . . . . . . . . . . . . . . . . . . . . . . . 92
7.3 Popup using edges. . . . . . . . . . . . . . . . . . . . . . . . . . 93
7.4 Simple example without interaction. . . . . . . . . . . . . . . . 95
7.5 Dynamically adding edges. . . . . . . . . . . . . . . . . . . . . 96

8.1 Graph with randomly placed nodes. . . . . . . . . . . . . . . . 100


8.2 Tree layout. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100
8.3 Circle layout applied on some elements . . . . . . . . . . . . . . 102
8.4 Some options of Circular Layout. . . . . . . . . . . . . . . . . . 102
8.5 Equidistant (left) and Weighted (right) layout with non-uniform
sizes. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
8.6 Flow and Grid Layouts. . . . . . . . . . . . . . . . . . . . . . . 105
8.7 RTFlowLayout alignments. . . . . . . . . . . . . . . . . . . . . 105
8.8 RTVerticalLineLayout and RTHorizontalLineLayout. . . . . . 105
8.9 RTTreeLayout demonstrating gap sizes. . . . . . . . . . . . . . 106
8.10 Comparison of Horizontal, Vertical and Radial Tree Layouts. . 107
8.11 RTDominanceTreeLayout showing dependencies between RTShape
classes. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108
8.12 Four trees clustered together. . . . . . . . . . . . . . . . . . . . 109
8.13 Sugiyama layout applied on hierarchy of ‘RTLayout‘ classes. . 110
8.14 RTForceBasedLayout used to layout hierarchy of classes. . . . 111
8.15 Pack of different elements. . . . . . . . . . . . . . . . . . . . . . 112
8.16 RTRectanglePackLayout used to layout a name cloud. . . . . . 113
8.17 Dependencies between classes. . . . . . . . . . . . . . . . . . . 114
8.18 Conditional layout. . . . . . . . . . . . . . . . . . . . . . . . . . 114
8.19 RTForceBasedLayout used to layout hierarchy of classes. . . . 115

10.1 Second version of the punchcard builder. . . . . . . . . . . . . 122


10.2 First version of the punchcard builder. . . . . . . . . . . . . . . 124
10.3 Second version of the punchcard builder. . . . . . . . . . . . . 125
xii List of Figures

10.4 Third version of the punchcard builder. . . . . . . . . . . . . . 127

11.1 A first example. . . . . . . . . . . . . . . . . . . . . . . . . . . . 130


11.2 The first example revisited. . . . . . . . . . . . . . . . . . . . . 131
11.3 Visualizing numbers and their connections. . . . . . . . . . . . 132
11.4 Cities and countries. . . . . . . . . . . . . . . . . . . . . . . . . 133
11.5 Visualizing a class hierarchy. . . . . . . . . . . . . . . . . . . . . 134
11.6 Each circle is a class and its color indicates the class packages. 136
11.7 Nesting elements. . . . . . . . . . . . . . . . . . . . . . . . . . . 136
11.8 Nesting elements. . . . . . . . . . . . . . . . . . . . . . . . . . . 137
11.9 Nesting colored elements. . . . . . . . . . . . . . . . . . . . . . 138
11.10Connecting nested colored elements. . . . . . . . . . . . . . . . 139
11.11Visualization of a file system. . . . . . . . . . . . . . . . . . . . 139

12.1 Simple scatterplot. . . . . . . . . . . . . . . . . . . . . . . . . . 142


12.2 Depth and magnitude of seisms greater than 2.5 during the
last 30 days. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143
12.3 Source code visualization. . . . . . . . . . . . . . . . . . . . . . 144
12.4 Two data sets in the same chart. . . . . . . . . . . . . . . . . . . 145
12.5 Axis configuration. . . . . . . . . . . . . . . . . . . . . . . . . . 146
12.6 Curves defined as functions. . . . . . . . . . . . . . . . . . . . . 148
12.7 Simple stack of data points. . . . . . . . . . . . . . . . . . . . . 148
12.8 Stacking data points. . . . . . . . . . . . . . . . . . . . . . . . . 149
12.9 Not stacked data set. . . . . . . . . . . . . . . . . . . . . . . . . 150
12.10Stacked data set. . . . . . . . . . . . . . . . . . . . . . . . . . . . 151
12.11Stacked data set. . . . . . . . . . . . . . . . . . . . . . . . . . . . 151
12.12Stacking multiple curves. . . . . . . . . . . . . . . . . . . . . . 152
12.13Data point aspect. . . . . . . . . . . . . . . . . . . . . . . . . . . 153
12.14Simple bar chart. . . . . . . . . . . . . . . . . . . . . . . . . . . 153
12.15Centered bar chart labels. . . . . . . . . . . . . . . . . . . . . . 154
12.16Coloring bars. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155
12.17Interaction in a bar chart. . . . . . . . . . . . . . . . . . . . . . . 156
12.18Horizontal line average. . . . . . . . . . . . . . . . . . . . . . . 156
12.19Horizontal line average. . . . . . . . . . . . . . . . . . . . . . . 157
xiii

12.20Example without Y-Axis translation. . . . . . . . . . . . . . . . 158


12.21Example with Y-Axis translation. . . . . . . . . . . . . . . . . . 159
12.22Date on the X-axis. . . . . . . . . . . . . . . . . . . . . . . . . . 160
12.23Multipoint example. . . . . . . . . . . . . . . . . . . . . . . . . 161
12.24A graph with a legend. . . . . . . . . . . . . . . . . . . . . . . . 162

13.1 Composition of two visualizations. . . . . . . . . . . . . . . . . 164


13.2 Giving a title to a group of elements. . . . . . . . . . . . . . . . 166
13.3 Two representations of the world population. . . . . . . . . . . 168

14.1 Simple use of a legend. . . . . . . . . . . . . . . . . . . . . . . . 172


14.2 Documenting a polymetric shape. . . . . . . . . . . . . . . . . 174

15.1 Mathematical description of SIR model using ODEs. . . . . . . 176


15.2 Visualizing Ebola cases in two African countries. . . . . . . . . 178
15.3 Loading Kendrick. . . . . . . . . . . . . . . . . . . . . . . . . . 179
15.4 Modeling the Measles model with Kendrick. . . . . . . . . . . 180
15.5 Mathematical description of the multi-hosts model using ODEs.181
15.6 Modeling Mosquito-borne model with Kendrick. . . . . . . . . 184
15.7 Representing the Ebola disease with Kendrick in 6 countries
of Africa. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 186
15.8 Visualizing a random network population with Kendrick. . . 188
15.9 Visualizing a scale-free network population with Kendrick. . . 189
15.10Visualizing a small-world network population with Kendrick. 190
15.11Visualizing a network contact model between groups of indi-
viduals with Kendrick. . . . . . . . . . . . . . . . . . . . . . . . 192

16.1 Simple example of using RTOSM. . . . . . . . . . . . . . . . . . 194


16.2 Pointing the camera to Paris. . . . . . . . . . . . . . . . . . . . 195
16.3 Adding a menu with locations. . . . . . . . . . . . . . . . . . . 196
16.4 Seismic activity on Earth. . . . . . . . . . . . . . . . . . . . . . . 197
16.5 Refugees monitored by the UNHCR. . . . . . . . . . . . . . . . 199

17.1 Use the plugin to load NLVA. . . . . . . . . . . . . . . . . . . . 204


17.2 Worst latency for each city. Note that Valencia and Nairobi
concentrate the most of the worst endpoints for connections. . 206
xiv List of Figures

17.3 Average latency for each city. . . . . . . . . . . . . . . . . . . . 206


17.4 MST shows what probably are physical wires connecting cities. 207
17.5 FarLow. Far cities that show low latency. Most East Coast
cities of the US are the best endpoints for European connec-
tions. A couple of unexpected results Joao Pessoa-Orlando
and Portland-Anchorage. . . . . . . . . . . . . . . . . . . . . . 207
17.6 Zooming-in on the Portland-Anchorage latency. . . . . . . . . 208
17.7 Latencies from Brisbane. . . . . . . . . . . . . . . . . . . . . . . 208
17.8 Latencies from ”Bern”, my actual location. . . . . . . . . . . . 209

18.1 Opening the Moose Panel. . . . . . . . . . . . . . . . . . . . . . 212


18.2 Loading checkstyle. . . . . . . . . . . . . . . . . . . . . . . . . . 214
18.3 Querying in the panel. . . . . . . . . . . . . . . . . . . . . . . . 215
18.4 Querying in the panel. . . . . . . . . . . . . . . . . . . . . . . . 217
18.5 Classes in the largest package. . . . . . . . . . . . . . . . . . . . 217
18.6 Visualizing cross-cutting concerns. . . . . . . . . . . . . . . . . 218
18.7 Dependencies between classes. . . . . . . . . . . . . . . . . . . 219
18.8 Inspecting a class group. . . . . . . . . . . . . . . . . . . . . . . 220
18.9 The pencil icon to open a system browser. . . . . . . . . . . . . 220
18.10A new entry has been added in the sub menu Visualize. . . . . 221
Chapter 1

Introduction

Computers plays a formidable extension of the human brain: a computer


liberates us from performing boring and repetitive tasks. Data visualization
is a wonderful field where computers nicely complements what the brain
excels at.
Conveying information through interactive visualizations is both an art
and a sophisticated engineering process. When crafting a visualization,
many decisions have to be taken based on a personal intuition or a care-
fully evaluated design aspect. Begin able to quickly experiment a new idea
is key in. Agile Visualization is about leveraging creativity by reducing the
cost associated to data and software visualization.
Challenges in data visualization does not actually involve visualizing
data. There are numerous books and software libraries for that purpose.
The challenge is in crafting a visualization that is easily reusable, compos-
able, and extensible. Agile Visualization addresses the need to easily reuse
visualizations and desire for many to reuse visualizations with their own
data.
Roassal is a visualization engine, written in the Pharo and VisualWorks
programming languages. All the examples provided in this book are there-
fore made for Roassal and are written in the Pharo programming language.
Since there is no better way than programming to craft a visualization,
readers are expected to have some programming experience to fully enjoy
Agile Visualization. This book is written for a large audience.
2 Introduction

1.1 Agile Visualization


Agile Visualization promotes the creation of a visualization based on very
short incremental steps. By "short" we mean a couple of seconds or minutes.
A data analysis is carried out by building a number of visualizations, from
which many are simply a try or have a very short usage time period. Reduc-
ing the creation time of a visualization to a few seconds or minutes greatly
increases the number of different paths taken by a data scientist to solve a
given problem.
A painter throws colors on a canvas as part of its activity. Colors may
be mixed, removed, and added at will. This metaphor may be considered
as the guiding line of Agile Visualization. Similarly to Agile Programming,
feedback should always occur a short time after the inception of a visualiza-
tion.

1.2 The Roassal visualization engine


Roassal is a visualization engine developed in Pharo. Roassal is also avail-
able for VisualWorks and is distributed under the MIT License.
Although Roassal regularly receives contributions from all over the
world, the effort behind Roassal is leaded at the University of Chile and at
Object Profile.
Roassal is developed and maintained in Pharo. Roassal is heavily main-
tained on the VisualWorks platform. Roassal runs also on BeeSmalltalk. The
book is mostly written using Pharo: examples and screenshots are made on
Pharo. VisualWorks developers will have to manually adapt the examples
and instructions.

1.3 Online and free version of this book


The website http://AgileVisualization.com contains a free version of the book.
An HTML version of each chapter is provided. Buying a physical copy of
Agile Visualization helps facing the cost involving in maintaining Roassal
and Agile Visualization.

1.4 The Roassal community


As with any successful open source project, Roassal is driven by an active
community effort. The positive aspect of this is that Roassal is evolving every
Other visualization platforms 3

day (literally). The negative aspect is that documentation becomes obsolete


as soon as it is published. The book has been written in such a way that deep
technical aspects are not discussed while general concepts are presented in
depth. The reason is that concepts are stable over time.
In case you wish to discuss some aspect of Roassal, need help, or simply
have a friendly chat, there is a number of ways to get in touch with us:

• The moose mailing list. http://moosetechnology.org is the website of the


Moose analysis platform. Details about how to join the Moose-Dev
mailing list are provided, in the community section. The community is
friendly, open-minded, and happy to help.
[email protected] is directed to the main developers of Roas-
sal.
• The Roassal team is also available from the channel #Roassal on the
Slack messaging. Information on joining the channel is available on
http://pharo.org/community.

• https://www.facebook.com/ObjectProfile contains many examples of


works carried out with Roassal.
• The Object profile Tweet account @ObjectProfile is also a great way to
share projects.

1.5 Other visualization platforms


Although Roassal, Pharo, and Moose are heavily promoted in this book, our
goal is not to make you switch language or visualization API. You probably
have a good reason for using what you are currently using. In case you are
seeking a fresh experience for visualizing and programming, then we cannot
recommend more this tool trilogy more.
The data visualization community has produced many visualization en-
gines. D3js, RaphaelJS, Processing, Flare are popular visualization frame-
works supported by a large community. Agile Visualization presents a com-
pelling use of visualizations in many different situations. We believe that
even if you are not a Roassal user, Agile Visualization will remain beneficial.

1.6 If you do not know Pharo


Pharo is easy to learn and use. It comes with fantastic programming tools
to make you intimately interact with objects; an object being an elementary
4 Introduction

computational unit. Our advice is to resist the natural tendency to map your
knowledge from Java or PHP into Pharo.

1.7 If you are already a Pharo/Smalltalk program-


mer
If you are a Pharo programmer with some graphical API knowledge, then
you may want to work with the core of Roassal (Part I). Once you under-
stand the basic functionalities of Roassal, we invite you to read more about
the builder infrastructure (Part II). Builder enables fantastic scripting and
visualization composition not seen anywhere else!
One last piece of advice before beginning your journey: do no try to build
any complex visualization in Roassal without first reading the builder infras-
tructure. Builders may ease your life!

1.8 Improving the book and Roassal


This book is the result of many long discussions, compromise, and collab-
orative efforts. Roassal and Agile Visualization are evolving fast and will
continue to do so. There is many ways you, the reader, can help us improve
the book. Here are a list of actions one can take to contribute to this global
effort:

• Let us know about your project. We will make sure that adequate ad-
vertisement will be made. All projects, ranging from a student effort to
an industrial case study, are relevant.
• As any piece of software, Roassal contains bugs. Reporting bug is a
great and easy way to help.
• Fixing bugs and proposing improvements.
• Buying a physical copy of this book contributes to support the engi-
neering effort and to prepare the next edition of the book.
• It is difficult for us to imagine all the different ways people will use the
artifacts promoted in this book. Actually, it would be presumptuous to
think so. All feedback is welcome. Let us know what you wish to read
and what you wish to read more about.
• Making a simple "Hello" on one of the multiple communication chan-
nels.
Acknowledgements 5

1.9 Acknowledgements
The book is the result of multiple and long-lasting collaborations. First of all,
we would like to thank the Lam Research company. Lam Research’s team
has always been supportive both morally and financially. Thanks CH and
Chris! You made this book a reality.
Some of the chapters are external contributions. We therefore thank their
authors:

• Bui Thi Mai Anh, Nick Papoulias and Serge Stinckwich for the chapter
Expressing Epidemiological Models.
• Leonel Merino for the chapter Network Latency.
• Yuriy Tymchuk for the chapter on Domain-Specific Visualization Made
Easy.
• Onil Goubier and Thierry Goubier for the chapter OpenStreetMap Inte-
gration.
• Peter Uhnák for the chapter Applying Layout.

Many people within the Moose, Pharo and ESUG communities have
deeply contributed to what is presented in the book. Your enthusiastic sup-
port and trust in what we do has always been invaluable.
We also would like to thank you, yes you, the reader, for your questions,
support, bug fixes, contribution, and encouragement.
We are deeply grateful to for their contributions to (no particular or-
der) CH Huang, Chris Thorgrimsson, Tudor Gîrba, Renato Cerro, Stéphane
Ducasse, Yuriy Tymchuk, Natalia Tymchuk, Juraj Kubelka, Juan Pablo San-
doval Alcocer, Milton Mamani, Vanessa Peña, Ronie Saldago, Alvaro Jose
Peralta, Pablo Estefo, Igor Stasenko, Faviola Molina, Ricardo Jacas, Daniel
Aviv Notario, Sergio Maass, Serge Stinckwich, Bui Thi Mai Anh, Nick Pa-
poulias, Johan Fabry, Nicolai Hess, Miguel Campusano, Peter Uhnák, Mar-
tin Dias, Jan Blizničenko, Samir Saleh, Nicolai Hess, Leonel Merino, Volk-
ert, Pierre Chanson, Andrei Chis, Thomas Brodt, Mathieu Dehouck, Miguel
Campusano, Onil Goubier, Thierry Goubier, Esteban Maringolo, Alejandro
Infante, Philippe Back, Stefan Reichhart, Ronie Salgado, Steffen Märcker.
Part I

Roassal
Chapter 2

Quick Start

This chapter gives an overview of what Roassal is up to. Many short code
snippets are provided and briefly described. These snippets are directly
copy-and-pastable within Roassal and typically each illustrates one partic-
ular aspect of the Roassal tooling.

2.1 Basic Installation

From scratch (novice)


Installing Roassal is relatively easy. It is just a matter of downloading a .zip
file, available on http://AgileVisualization.com. Unzip the file and execute Moos-
eSuite to immediately start Roassal. Note that your filesystem will not be
polluted: the application you have downloaded is all you need.

Loading from Pharo (for advanced user)


Pharo is a modern object-oriented programming language. If you are a Pharo
programmer and want to integrate Roassal into your working developing en-
vironment, you may be interested in loading Roassal directly from its repos-
itory.
Roassal is available within the Catalog browser, available from the Tools
menu entry. In case of you need a Gofer script (if you wish to programmati-
cally load Roassal or create a dependency within your application):
Gofer it
smalltalkhubUser: 'ObjectProfile' project: 'Roassal2';
configurationOf: 'Roassal2';
loadStable.
10 Quick Start

Roassal is known to work on Pharo 4.0, 5.0, and 6.0.

Linux Ubuntu Users


Roassal relies on Cairo to render visualization. While the Pharo virtual ma-
chine is shipped with the Cairo library for the Mac OSX and Windows plat-
forms, linux users need to install it manually. This is easily done with the
following incantation:
$ sudo apt-get install libcairo2:i386

Note that you need administrator access grant the Cairo library installa-
tion. You will need to manually install Cairo if you are using Ubuntu, De-
bian, or a fork such as Mint.

Roassal on VisualWorks
Roassal is available on the public Cincom Store, under the bundle roassal2-
full. Note that you need to have Cairo installed from your VisualWorks dis-
tribution.

2.2 Running Roassal


Dragging-and-dropping the file moose.image on the virtual machine opens
the Moose programming environment. Moose is a platform for data and
software analysis. Roassal is the visualization engine part of Moose. Moose
and Roassal are written in the Pharo programming language.

2.3 First visualization


Most of the visualizations given in this book are written as a short script. A
playground is a tool offered by Pharo to execute script. A playground may
be open from the World menu (i.e., the menu you get when you click outside
a Pharo window):
Open a playground and type the following (or copy if you are using an
online version of the book):
b := RTMondrian new.
b shape label.
b nodes: (1 to: 100).
First visualization 11

Figure 2.1: Opening the playground.

Figure 2.2: Connecting numbers.

b edges connectFrom: [ :i | i // 2 ].
b layout cluster.
b
12 Quick Start

Press the Do it all and go button, represented with a green triangle, on the
top right of the Playground window. Alternatively, select the whole content
of the playground with Ctrl-A or Cmd-A and press Ctrl-G or Cmd-G. You
should obtain Figure 2.2.
Mondrian is a high level code library for building expressive and flexible
visualizations. After selecting the Mondrian library, the script selects the
label shape. Nodes are then created, numbered from 1 to 100. Edges are
built as follows: for each number i between 1 and 100, an edge is created
from the element representing i // 2 and i. The expression a // b returns the
quotient between a and b, e.g., 9 // 4 = 2 and 3 // 2 = 1. Nodes are then
ordered as a cluster layout. Each element is draggable and has a popup with
the mouse.

2.4 Visualizing the file system


We will reuse the previous visualization to visualize a file system instead
of arbitrary numbers. Pharo offers a large library to manipulate files and
folders. Integrating files in a Roassal visualization is easy. Consider the fol-
lowing script:
path := '/Users/alexandrebergel/Documents'.
allFilesUnderPath := path asFileReference allChildren.
b := RTMondrian new.
b shape circle
color: Color gray trans;
if: [ :aFile | aFile path basename endsWith: '.pdf' ] color: Color red trans.
b nodes: allFilesUnderPath.
b edges connectFrom: #parent.
b normalizer
normalizeSize: #size min: 10 max: 150 using: #sqrt.
b layout cluster.
b

The variable path contains a location in your file system. You need to
change the path to execute the script. Please note that indicating a large
portion of the file system may significantly increase the computation time.
The expression path asFileReference converts a string indicating a path as a
file reference. FileReference is a Pharo class that represents a file reference,
typically locally stored, on the hard disk. The message allChildren gets all the
files recursively contained in the path. The visualization paints in red files
for which their name ends with .pdf.
Compared with the previous example, this visualization uses a normal-
izer to give a size to each circle according to the file size. The size varies
from 10 to 150 pixels, and uses a square root (sqrt) transformation to cope
Geographical CSV data 13

Figure 2.3: Visualizing a file system.

with disparate size. You may want to click on the camera center icon button
just above the visualization to scale it down.
As a small exercise, you can replace #sqrt by [:s | (s + 1) log * 2 ], for a
logarithm scale, or #yourself for a linear scale.

2.5 Geographical CSV data


Comma-separated values (CSV) is a file format commonly manipulated by
spreadsheet applications, such as Excel. Roassal has facilities to easily extract
data from CSV files. The following example shows earthquakes over the last
30 days:
tab := RTTabTable new input: 'http://earthquake.usgs.gov/earthquakes/feed/v1.0/
summary/2.5_month.csv' asUrl retrieveContents usingDelimiter: $,.
tab removeFirstRow.
tab replaceEmptyValuesWith: '0' inColumns: #(2 3 4 5).
tab convertColumnsAsFloat: #(2 3 4 5).

b := RTMapLocationBuilder new.
b shape circle
size: [ :m | 2 raisedTo: (m - 1) ];
color: (Color red alpha: 0.3).
tab values do: [ :row | b addPoint: row second @ row third value: row fifth ].
14 Quick Start

Figure 2.4: Seismic activity the last 30 days.

You should obtain a picture resembling Figure 2.4.


Sending the message asUrl to a string returns an instance of the class Url,
describing urls. The content of the url is obtained by sending retrieveContents.
For example, if you wish to know what google.com is made of, inspect the
following expression: ’http://google.com’ asUrl retrieveContents.
The data visualized in the previous example is from the US Earthquake
Hazards Program (http://earthquake.usgs.gov/earthquakes/feed/v1.0/). The re-
trieved data looks like the following lines:
'DateTime,Latitude,Longitude,Depth,Magnitude,MagType,NbStations,Gap,Distance,
RMS,Source,EventID,Version
2014-07-31T12:56:24.800+00:00,38.741,-122.714,1.2,0.9,Md,8,112,,0.04,nc,
nc72268926,1406813044917
2014-07-31T12:49:08.000+00:00,67.655,-162.002,15.5,3.5,ml,,,,0.65,ak,
ak11344820,1406813786856
2014-07-31T12:49:01.000+00:00,59.713,-142.589,99.9,1.4,ml,,,,5.95,ak,
ak11344819,1406813766175'

Making that bunch of data exploitable requires a number of conversions


and manipulations. The class RTTabTable is made to convert and extract val-
ues from the data. We first remove the header (’DataTime,Latitude,...’). Empty
values are converted with ’0’. Portion of the data matching ’„’ is transformed
into ’,0,’. Since the table is composed of string characters, we need to convert
them as numerical floats. Column 2, 3, 4, and 5 are converted into float num-
bers (i.e., decimal numbers, such as 4.5). We create a RTMapLocationBuilder,
which will be fed with the extracted values. We create a new shape, which
Seism activity over time 15

Figure 2.5: Timeline of earthquakes.

is a transparent circle. The radius of each circle represents an earthquake of


magnitude m, given by 2m−1 . This formula is rather arbitrary but it gives an
intuitive output.

2.6 Seism activity over time


Timelines are useful abstractions to communicate evolution of particular
properties or metrics over the time. Using the same example, the following
script produces a time line of the earthquakes (Figure 2.5):
tab := RTTabTable new
input: 'http://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/2.5
_month.csv' asUrl retrieveContents
usingDelimiter: $,.
tab removeFirstRow.
tab convertColumn: 1 to: [ :s | (DateAndTime fromString: s) julianDayNumber ].
tab convertColumnsAsFloat: #(5).

v := RTView new.
es := RTEllipse elementsOn: tab values.
v addAll: es.
es @ RTPopup.

RTMetricNormalizer new
elements: es;
normalizeColor: #fifth using: { Color orange . Color red };
alphaColor: 0.3;
normalizeX: #first min: 0 max: 600;
normalizeSize: #fifth min: 0 max: 80 using: [ :mag | 2 raisedTo: (mag - 1) ].

es @ (RTLabeled new text: [ :row | row fifth > 6 ifTrue: [ row fifth ] ifFalse: [ '' ] ]).
v @ RTDraggableView.
v

The first column is converted into Julian day numbers. This is useful for
ordering the earthquakes later on. The fifth column is then converted as a
float. A circle is associated with each entry of the table. The size of each
16 Quick Start

ellipse and its colors is deduced from the fifth column (earthquake magni-
tude). The first column, the Julian day number, is used to horizontally order
the ellipse. Each earthquake with a magnitude greater than 6 has a title.
The Julian Day Number (JDN) is the number of elapsed days since the
beginning of a 7,980-years cycle. This number is frequently used by as-
tronomer. The starting point for the first Julian cycle began on January 1,
4713 BC.

2.7 Charting
Roassal has a sophisticated mechanism to draw charts. Grapher takes as in-
put a set of data points and a configuration (which may be minimal) to ren-
der these data points.

Plotting
The following example illustrates Ebola outbreaks (Figure 2.6):
"Preparing the data"
tab := RTTabTable new input: 'http://bit.ly/EbolaCSV' asUrl retrieveContents
usingDelimiter: $,.
tab removeFirstRow.
tab replaceEmptyValuesWith: '0' inColumns: #(10 11).
tab convertColumnsAsInteger: #(10 11).
tab convertColumnsAsDateAndTime: #(3 4).
data := tab values reversed.

"Charting the data"


b := RTGrapher new.

ds := RTData new.
ds interaction fixedPopupText: [ :row | row value at: 12 ].
ds dotShape ellipse
color: (Color blue alpha: 0.3);
size: [ :row | (row at: 11) / 5 ].
ds points: data.
ds connectColor: Color blue.
ds y: [ :r | r at: 10 ].
ds highlightIf: [ :row | (row at: 10) > 100 ] using: [ :row | row third year ].
b add: ds.

b axisX noLabel; numberOfTicks: tab values size.


b axisY noDecimal.
b
Charting 17

Figure 2.6: Ebola fatalities.

Figure 2.6 gives the output of the script. The Y-axis represents the number
of fatalities. The X-axis is the sequential events. Note that time is not scaled
in this chart, as the data are simply stacked from left to the right. The size of
an event gives the rate of fatalities. Locating the mouse above an event gives
some contextual information.
The CSV data is obtained from a short bit.ly link we have set for the pur-
pose of keeping the script short. The original data comes from the website
http://mapstory.org. MapStory is a website with short and illustrative stories.
Outbreaks are contained in the file in a reverse chronological order, which
is why we first reverse order it. Each point is a row in the table. The use of
fixedPopupText: opens a popup for each data point when the mouse is above
the data point. Column 12 of a row is a description of the outbreak.
Each data point is a circle. Its size reflects the value of the column fatality
rate (% of death per contamination). The column 10 indicates the number
of fatalities. This value is used on the Y-axis. Only major events are labeled
with the year (the third column corresponds to the year of the outbreak).

Double charting
Charter offers several visualization types. A double bar chart is an effective
visual representation for small datasets and with two metrics to be repre-
sented. Consider the following example that illustrates the distribution of
people in North American states (Figure 2.7).
tab := RTTabTable new input: 'http://bit.ly/CensusGov' asUrl retrieveContents
usingDelimiter: $,.
tab removeFirstRow.
tab convertColumnsAsInteger: #('POPESTIMATE2013' 'POPEST18PLUS2013').
18 Quick Start

Figure 2.7: Population per state in the USA.

b := RTDoubleBarBuilder new.
b pointName: [ :row | row at: (tab indexOfName: 'NAME') ].
"Remove the first line, the sum"
b points: tab values allButFirst.
b bottomValue: [ :row | ((row at: (tab indexOfName: 'POPESTIMATE2013')) /
1000) asInteger ]
titled: 'Pop estimate (x 1000)'.
b topValue: [ :row | ((row at: (tab indexOfName: 'POPEST18PLUS2013')) / 1000)
asInteger]
titled: 'Pop +18 estimate (x 1000)'.
b

Data are obtained from http://census.gov, a wonderful source of social data.


Some columns are converted as numerical integers. Rows are then provided
to the builder b. The first line is removed since it contains the value for the
whole United States, which would distort the visualization.

Multiple graphs
Several graphs may be represented simultaneously. Consider the following
example (Figure 2.8):
Charting 19

Figure 2.8: Multiple graphs.

b := RTGrapher new.
numberOfDataSets := 5.

colorNormalizer := RTMultiLinearColorForIdentity new


objects: (1 to: numberOfDataSets).
1 to: numberOfDataSets do: [ :i |
ds := RTData new.
ds noDot.
ds points: ((1 to: 500) collect: [ :ii | 50 atRandom - 25 ]) cumsum.
ds connectColor: (colorNormalizer rtValue: i).
b add: ds.
].
b

The variable numberOfDataSets indicates the amount of data points we


randomly generate. The variable colorNormalizer points with a color normal-
izer. The job of a color normalizer is to associate an object to a color. In
this case, objects are numbers, ranging to 1 to numberOfDataSets. The default
color palette is used.
Data points are obtained by summing random values from 1 to 500 and
using a cumulative sum. We remove the dots (using noDot) to solely have
line curves.
20 Quick Start

Figure 2.9: Gantt chart.

2.8 Timeline
Timelines are essential when representing process executions. A Gantt chart
is commonly employed to represent project development and implementa-
tion. Consider the following script (Figure 2.9):
data := #(
#(WP1 0 4) #(WP2 4 8)
#(WP3 8 12) #(WP4 3 4)
#(WP4 7 9) #(WP4 10 12)
).
b := RTTimeline new.
s := RTTimelineSet new.
s objects: data.
s lineIdentifier: #first.
s start: #second.
s end: #third.
b add: s.
b axisX
noDecimal;
title: 'Month';
numberOfLabels: 12.
b

Objects that will be represented are given by the variable data. Each object
is represented as an array of 3 elements, for example #(WP1 0 4). The name
of an event is given by the first element of the array. The beginning of an
object is given by the second element of the array and the event end by the
third position. There is no need to have decimal values on the X-axis since
only integer values are considered here.
Integration with OpenStreetMap 21

2.9 Integration with OpenStreetMap


OpenStreetMap is a collaborative project to create maps (http://openstreetmap.
org). One of the advantage of OpenStreetMap is that it offers an API to access
and download map tiles.
Consider the following example that associate bar charts to some geo-
graphical location (Figure 2.10):
v := RTView new.
v @ RTDraggableView.
map := RTOSM new.
v add: map element.

"City geographical positions obtained from Wikipedia"


paris := 48.8567 @ 2.3508.
newyork := 40.7127 @ -74.0059.
london := 51.507222@ -0.1275.

"Some arbitrary data"


data :=
{ { paris . #(10 5 10 3 10 6 8) } .
{ london . #(5 3 3 -5 ) } .
{ newyork . #(5 -2 10 15 -10) } }.

data do: [ :tupple |


| grapher dataSet |
grapher := RTGrapher new.
grapher extent: 150 @ 100.
dataSet := RTData new.
dataSet points: tupple second.
dataSet barShape width: 10; color: Color red.
grapher add: dataSet.
grapher build.

barElements := grapher view elements.


v addAll: barElements.
barElements translateTo: (map latLonToRoassal: tupple first) ].

v canvas camera translateTo: (map latLonToRoassal: paris).


v canvas camera noInitializationWhenOpen.
v canvas camera scale: 0.3.
v

After a basic initialization, data are plotted as a bar chart on top of the
tiles. Zooming in and out apply on both the map and the graph. The view
initially focuses on Paris, when opened.
Roassal supports a wide range of charts. Another example is (Figure
22 Quick Start

Figure 2.10: OpenStreetMap integration.

2.11):
v := RTView new.
v @ RTDraggableView.
map := RTOSM new.
v add: map element.

"Place to set the data and center the camera"


london := 51.507222@ -0.1275.

"Some arbitrary data"


data := ((1 to: 500) collect: [ :i | 50 atRandom - 25 ]) cumsum.

"We build the graph"


b := RTGrapher new.
b extent: 100@30.
d := RTData new.
d noDot.
d connectColor: Color red.
d points: data.

b add: d.
b axisY
labelFontHeight: 6;
Integration with OpenStreetMap 23

Figure 2.11: Charting on top of London.

color: Color red;


title: 'Sale'.

b axisX color: Color red; noTick; title: 'country'.


b build.
elementsAndEdges := b view elements, b view edges.

"We create a white background"


whiteBackground := (RTRoundedBox new color: Color white trans; borderRadius:
10) element.
v add: whiteBackground.
v addAll: elementsAndEdges.
RTNest new on: whiteBackground nest: elementsAndEdges.
whiteBackground translateTo: (map latLonToRoassal: london).

v canvas camera translateTo: (map latLonToRoassal: london).


v canvas camera noInitializationWhenOpen.
v canvas camera scale: 1.5.
v
24 Quick Start

Figure 2.12: Example browser.

2.10 More examples


Many examples are available within the Roassal distribution. The Roassal
example browser is available from the World menu. Over 1,000 examples
are available (Figure 2.12).
Most of the examples given above use a dedicated builder. Builders are a
high level abstract on top of the Roassal low-level API. Part I of the book fo-
cuses on the core of Roassal, and covers this low-level API. Part II of the book
details the builder mechanism. Part III presents some large applications of
Roassal.
Chapter 3

Pharo In a Nutshell

Programming is an important skill to properly tell a computer what it has to


do. This chapter gives an introduction of the Pharo programming language.
Although we tried to make this chapter smooth and easy to read, having
some basic programming knowledge is expected.
Pharo is an object-oriented programming language, class-based, and dy-
namically typed. The chapter therefore begins with a brief introduction of
what programming with objects is all about. The focus will subsequently
moves toward Pharo.

3.1 Going over an example, Step-by-step


Instead of giving a long rhetorical description about object-orientation, let
us pick a simple example. The following code snippet opens a Roassal view
with a label showing ’Hello World’:
v := RTView new.
v add: (RTLabel elementOn: 'Hello World').
v

To execute the script given above, you need to type it in a playground,


and press the green triangle (Figure 3.1). This script displays the message
Hello World. The word RTView refers to a class. A class is an object factory
and its name is easily recognizable because of its first letter, which is always
a capital letter. A class is like a baking pan for cakes: creating an object is
like backing a cake. All the cakes produced by a pan have the same physical
aspects, but attributes, such as ingredients, may vary.
The first line creates an object view. An object is created using the mes-
sage new. For example, the expression RTView new creates a view, String new
26 Pharo In a Nutshell

Figure 3.1: Hello World.

Figure 3.2: Circles are lined up.

creates an empty string character, Color new creates a black color. The view,
produced by executing RTView new, is said to be the object (for instance) pro-
duced by RTView.
The expression object asString sends the message asString to an object, ref-
erenced by the variable object. In Pharo, a class is also an object, which means
that objects are created by sending a message to a class. The message new is
sent to the class RTView, which has the effect of creating an object. This object
is assigned to the variable v using the operator := .
In the second line, the message elementOn: is sent to the class RTLabel. An
argument is provided to that message, which is a string character ’Hello World’
as argument. These Roassal instructions simply creates an element that has a
shape label. That element is passed as argument to the message add:. The ef-
fect of add: is simply to add the element in the view. The view, referenced by
v, understands the message add: because the class RTView defines a method
add:.
Consider this script (Figure 3.2):
values := #(20 40 35 42 10 25).
v := RTView new.
elements := (RTEllipse new size: #yourself) elementsOn: values.
v addAll: elements.
RTHorizontalLineLayout on: elements.
elements @ RTLabeled.
v

This example renders 6 circles, each having a proper size. The expres-
sion #(20 40 35 42 10 25) defines an array, containing a few numbers. The
expression RTEllipse new size: #yourself creates an object of the class RTEllipse
Pillars of Object-oriented programming 27

by sending the message new. The message size: is sent to that ellipse ob-
ject, with the symbol #yourself as argument. This message size: configures
the size of the ellipses: The size of each circle is computed with the model
object when creating the element. In particular, the message #yourself will be
sent to each element of the array values. For example, the size of the circle
representing the value 35 has a diameter of 35 pixels. Circles are then lined
up using a dedicated layout, invoked by sending the message on: to it with
the elements as argument. The expression elements @ RTLabeled labels each
elements.
Most visualization engines and data analysis environments operate on
the principle illustrated above: scripts are typed in a workspace or a web-
page, and executed to produce a visualization. This approach to build a
visualization or a small application is appealing since it is self-contained:
all one needs to know is within the linear script and the expressed logic is
made explicit. However, this way of developing software artifacts has seri-
ous limitations. Maintenance and evolution are seriously diminished. For
example, a 200-line long script is painful to modify and confusing to look at.
If not properly structured, adapting a complex visualization may have the
fantastic ability to consume a ridiculously large amount of time. This is a sit-
uation well known to journalists, data scientists, and also software engineers.
Fortunately, a couple of decades ago the Software Engineering research com-
munity produced a way of programing that is able to cope with the inherent
complexity of software artifact development. Object-oriented programming
is the most successful way to handle complex and large development.

3.2 Pillars of Object-oriented programming


Object-oriented programming simplifies the programming activity. Han-
dling objects, instead of functions or code snippets, uses a metaphor that
is familiar to us, humans: an object may react upon some actions, have a
behavior on its own, and may hide details about how it is physically built.
Let us bring a bit of theory in all this. There are five essential ingredients
to an object-oriented system:

• Encapsulation: in our daily life, we are used to handling information


that is not publicly accessible: social security numbers, body weight,
just to name a few. Encapsulation in object-oriented systems is about
letting objects have private information. Private information may re-
flect detail that is not directly necessary to a service consumer. In case
private information has to be publicly exposed, asking a question is a
polite and cordial way to access it. In object-oriented programming,
asking a question or giving an order is called sending a message. Encapsu-
28 Pharo In a Nutshell

lation in object-oriented programming enables abstractions and infor-


mation hiding.
• Composition: a complex problem may be solved by cutting it down into
smaller problems, hopefully easier to solve.
• Distribution of responsibility: in our daily life, each of us has duties and
responsibilities. Having a clear separation of concerns is key to hav-
ing a good object-oriented design. This is what makes systems easy
to understand and maintain. For example, instead of asking some-
one’s weight in order to select what may be eaten, it is better for ev-
erybody to let that person make a responsible choice. This example
is not far stretched: many difficulties in software maintenance are di-
rectly rooted from improperly assigned responsibilities in software.
• Message sending: electronic mails are the base of our daily communi-
cation: a person, called the sender, sends an email to another person,
called the receiver. In object-oriented programming, objects commu-
nicate in a similar fashion: computation is carried out by sending mes-
sages between objects. An object sends messages to other objects. After
sending a message, a reply is returned. In object-orientation, sending
a message is often perceived as a way to delegate responsibility.
• Inheritance: general concepts have to be specialized to address partic-
ular requirements. Inheritance allows one to define conceptual hierar-
chy, reuse code, and support polymorphism. Inheritance may say that
an ellipse and a square are two graphical shapes.

These five pillars are not particularly tied to a programming language.


So, in theory, it is perfectly doable to have an object-oriented design in a
procedural language such as C. However, having a programming language
that enforces these principles greatly alleviates the task of the programmer.
There are numerous object-oriented languages around and Pharo is one
of them. Pharo differs from other languages by offering an homogeneous
way of expressing computation: everything is an object, therefore computa-
tion happens by sending messages. When objects are taken seriously, there
is no need for primitive types (e.g., int, float), language operators, and even
external compilable files! Considering only message sending significantly re-
duces the amount of technological details associated with the program exe-
cution that most mainstream programming languages unnecessarily expose.

3.3 Message sending


Sending a message is the elementary unit of computation. Understanding
how to send a message is key to feel comfortable in Pharo. Consider the
Message sending 29

expression:
'the quick brown fox jumped over the lazy dog' replaceAllRegex: 'fox' with: 'cat'

This expression sends to the string object ’the quick brown fox jumped over
the lazy dog’ a message having the name #replaceAllRegex:with: and two argu-
ments, ’fox’ and ’cat’, themselves two string objects. The result of sending this
message is ’the quick brown cat jumped over the lazy dog’, another string.
In Pharo, a character string (often simply called a string) is a collection of
characters written between two accents (e.g., ’fox’). A string is a plain object,
which means one can send messages to it. A message is composed of two
essential ingredients: a name and a collection of arguments. It may be that
the set of arguments is empty. For example, the expression ’fox’ asUppercase,
which evaluates to ’FOX’, sends the message #asUppercase to the string ’fox’.
No arguments are involved here.
Message sending is at the heart of the Pharo language, and is therefore
well expressed within its syntax. There are three kinds of message sending:

• Unary message: a unary message is a message that does not take any
argument. The expression ’fox’ asUppercase sends a unary message to
the string ’fox’.

• Binary message: a binary message has exactly one argument and its
name is not made of alphanumerical characters. Instead one or two
characters are common for binary messages, such as +, /, -, <, >>. The
expression 2 + 3 sends to the object 2 a binary message named + with
the argument 3. You may notice that this expression has therefore a
different semantic than 3 + 2, although the result is obviously the same.
Note that the expression 3 + 2 * 2 returns 10, and not 7 as one may
expect. If you wish to enforce mathematical priorities in arithmetic
operations, use parenthesis, as in 3 + (2 * 2).

• Keyword message: a keyword message is a message that is neither unary


nor binary. A keyword message accepts one or more object arguments.
Consider the example ’the quick brown fox jumped over the lazy dog’ in-
cludesSubstring: ’fox’. This expression evaluates to true. The name of
the keyword message is #includesSubstring: and the argument is ’fox’.
Each argument is preceded by a keyword. For example, the message
replaceAllRegex: ’fox’ with: ’cat’ contains two keywords and therefore two
arguments. Arguments are inserted within the message name.

Sending a message triggers a mechanism that searches for a method to


execute. This mechanism, often called "method lookup", begins from the
class of the object up and goes to the superclass if not found.
30 Pharo In a Nutshell

3.4 Object creation

An object is a bundle of data to which messages can be sent to. An object


is created most of the time by sending the new message to a class. This is
revealing the true nature of classes, being an object factory. A class may
produce as many different objects as new is sent to it. Objects produced from
a unique class are different but understand the same set of messages and
have the same variables. Differences between two or more objects issued
from the same class are the the values given to these variables. For example,
consider the following expression:

Object new == Object new

This expression sends three messages, twice the message new and once
the message ==, used to compare object identities. The expression evaluates
to false, since the two objects are different, i.e., located at different physical
memory location.
The expression Point new creates a point by sending the message new to
the class Point. There are several ways to create a point:

• Point new creates a point (0, 0). All classes in Pharo understand the
message new. Except when explicitly prohibited, an object is created
by sending new to the class.

• Point x: 5 y: 10 creates a point (5, 10). This expression sends the message
x:y: to the class Point, with 5 and 10 as arguments. The class Point defines
the class method x:y:. The difference between new and x:y: is that the
latter allows one to create and initialize a point with a given value for
x and y.

• 2 @ 3 sends to the object 2 the message named @ with the argument 3.


The effect is the same than Point x: 2 y: 3, which is to create the point (2,
3).

Each class has its way to create objects. For example, a point is not created
the same way as is a color. Creating an object is also commonly mentioned
as "instantiating a class" and an object is often referenced as "instance".
A class is an object factory and an object is necessarily created from a
class. An object associates values to the attributes defined by the class of
the object. As discussed above, objects interact by sending messages. An
object is able to understand messages corresponding to methods defined in
its class, and methods defined in the chain of superclasses.
Class creation 31

3.5 Class creation


A class is a factory of objects, often regarded as an abstraction of objects. You
need to create classes as soon as you wish to bundle logic and data together
(i.e., "doing hands on work").
A class belongs to a package. You may want to create a dedicated pack-
age to contain the classes you will define. A package is created by right-
clicking on the package list in a system browser. We will define a class Tweet:

1. Open a system browser from the World menu

2. Right click on the top left list panel, and define a package called Tweet-
sAnalysis.

3. Create the class Tweet. Classes are created by filling the following tem-
plate in a code browser:

Object subclass: #NameOfSubclass


instanceVariableNames: ''
classVariableNames: ''
package: 'Announcements-Core'

The text NameOfSubclass has to be replaced by the name of the class you
wish to create. After the keyword instanceVariableNames: you need to provide
the instance variables, and after classVariableNames: the class variables. Right
click on the code and select the menu accept to effectively create the class.
You should have
Object subclass: #Tweet
instanceVariableNames: 'content sender date'
classVariableNames: ''
package: 'TweetsAnalysis'

You should obtain something similar to Figure 3.3. We have defined the
class Tweet, contained in the package TweetsAnalysis. The class contains three
instance variables, content, sender, and date. No methods have been defined
so far. Note that in Pharo, an instance variable name begins with a minuscule
letter.

3.6 Method creation


A method is an executable piece of code. A method is composed of instruc-
tion statements typically aiming to carry out a computation. We will define
32 Pharo In a Nutshell

Figure 3.3: The class Tweet.

a small mathematical example to illustrate the creation of a method. We will


therefore leave out our Twitter example for a short while.
The Fibonnacci sequence is a well known sequence of numbers obtained
with the formula F(n) = F(n-1) + F(n-2). Terminal cases are given with F(0) = 0
and F(1) = 1.
We will implement the Fibonacci formula as a method defined on the
class Integer. This class describes all the integer numbers in Pharo. First, let
us open a system browser on this class. Spotter is a tool for searching in
Pharo (Figure 3.4). We will therefore search for the Integer class and opens a
system browser on it.
Enter Integer in Spotter and select the corresponding class by pressing the
Enter key or clicking on it using the mouse. Select the arithmetic protocol
(third list panel) and enter the following code in the lower text pane:
fibonacci
self <= 1 ifTrue: [ ^ self ].
^ (self - 1) fibonacci + (self - 2) fibonacci

After having entered the code, right click on it and select Accept. Accept-
ing a method compiles it and makes it executable.
Open a playground, type and execute 10 fibonacci. You will see 55, its
result (Figure 3.5).
The self word refers to a pseudo-variable that designates the object hav-
ing received the message. When executing the expression 10 fibonacci, self
Method creation 33

Figure 3.4: The World menu and Spotter.

Figure 3.5: Fibonacci of 10.


34 Pharo In a Nutshell

refers to the object 10. The expression self <= 1 is true if self is either 1 or
smaller. If this is the case, then we exit the method with ifTrue: [ ↑ self ]. The
caret character (↑) is a return statement: it exits the method and returns a
value. If self is greater or equals 2, then the result is the sum of (self - 1)
fibonacci and (self - 2) fibonacci.
Another common pseudo-variable is super. The two pseudo-variables self
and super reference the same object, the object that has received a message.
The unique difference between self and super is characterized when when
one sends a message to it, in particular:

• sending a message to self triggers the method lookup from the class of
the object,
• sending a message to super triggers the method lookup from the super-
class of the class that contains the call on super.

Coming back to our Tweet example. Define the following six methods on
the class Tweet:
date
^ date

date: aDate
date := aDate

content
^ content

content: aContent
content := aContent

sender
^ sender

sender: aSender
sender := aSender

These methods will enable one to set the content of a tweet and query
about it.
Click on the Class button in the system browser. Clicking on it switches
the system browser to the class side: methods defined on that side are class
methods of the class Tweet. Define the method (Figure 3.6):
createFromURL: urlAsString
"Method to be defined on the CLASS side"
| content lines sender date |
content := (ZnEasy get: urlAsString) contents readStream.
Method creation 35

lines := content contents lines collect: [ :l |


| firstCommaIndex secondCommaIndex |
firstCommaIndex := l indexOf: $,.
secondCommaIndex := l indexOf: $, startingAt: (firstCommaIndex + 1).
sender := l copyFrom: 1 to: (firstCommaIndex - 1).
date := l copyFrom: (firstCommaIndex + 1) to: (secondCommaIndex - 1).
content := l copyFrom: (secondCommaIndex + 1).
{ sender . date . content }
].
^ lines collect: [ :line |
Tweet new
sender: line first;
date: line second;
content: line third
]

The method createFromURL: fetches a CSV file we have prepared for that
example. The file contains 1000 random tweets. It does a simple parsing of
the content by identifying the comma.
Next, you can define the method:
createFromExample
"Method to be defined on the CLASS side"
^ self createFromURL: 'http://bit.ly/exampleTweetCSV'

The url http://bit.ly/exampleCSV is an example we have prepared to illus-


trate our purpose. Open it in a web browser to see what it looks like. At
that stage, evaluating the expression Tweet createFromExample returns a list
of 1000 tweet objects, each tweet describing an entry of the online CSV file.
We will define two new methods on the class Tweet. Switch to the in-
stance side (i.e., unselect the Class button in the system browser), and define
the following two instance methods:
words
^ self content substrings

isSimilarTo: aTweet
^ (self words intersection: aTweet words) size >= 6

The method words simply returns all the words defining the content of
a tweet. It uses substrings which returns a list of words from a string. For
example, the expression ’fox and dog’ substrings return #(’fox’ ’and’ ’dog’). The
method isSimilarTo: takes as argument another tweet and returns true or false
whether the tweet argument is similar to the tweet that receives the message
isSimilarTo:. The notion of similarity we use here is: two tweets are similar if
they have at least 6 words in common.
36 Pharo In a Nutshell

Figure 3.6: The method createFromURL:.

So, we have some objects and a way to establish a relation between them.
This is more than enough to start to visualize them. Open a playground and
type (Figure 3.7):
tweets := Tweet createFromExample.
positive := tweets
select: [ :t | #('great' 'cool' 'super' 'fantastic' 'good' 'yes' 'okay' 'ok') includesAny:
t words ].

negative := tweets
select: [ :t | #('bad' 'worse' 'down' 'no') includesAny: t words ].

b := RTMondrian new.
b shape circle
if: [ :t | positive includes: t ] color: Color blue;
if: [ :t | negative includes: t ] color: Color red.
b interaction popupText: #content.
b nodes: positive, negative.
b edges connectToAll: [ :tweet |
tweets select: [ :t | t isSimilarTo: tweet ] ].
Block closure 37

Figure 3.7: Positive and Negative tweets.

b layout forceWithCharge: -200.


b normalizer normalizeSize: [ :tweet | tweet content size ];
alphaColor: 0.4.

The code given above does a very primitive classification of feeling.


Tweets with a positive feeling are blue, while the negative ones are red.
Among the 1000 tweets, only 60 have a feeling.
We see that only a few of the tweets have actually common words and
most of them are negative.

3.7 Block closure


A block closure (also simply called "block") is a piece of code associated to
an environment. A block is manipulable, as any object is (i.e., a block may
therefore be provided as message argument and be assigned to a variable).
The expression [ :value | value + 5 ] is a block closure that takes one parameter
and return the sum between that argument and 5. This block may be eval-
uated with an argument using the message value:. Consider the following
code snippet:\
38 Pharo In a Nutshell

b := [ :value | value + 5 ].
b value: 10. "Return 15"
b value: -5. "Return 0"

Recall the definition of the fibonacci method, defined on the class Integer:
fibonacci
self <= 1 ifTrue: [ ^ self ].
^ (self - 1) fibonacci + (self - 2) fibonacci

The message ifTrue: takes a block [ ↑ self ] as argument. In case that self
<= 1 evaluates to true, the block is evaluated and triggers an early exit of the
method. The expression ↑ self exits the method. The block uses the pseudo-
variable self. A block may access variables defined in the outer lexical scope.
A block may use temporary variables, instance variables, and argument vari-
ables.

3.8 Control Structure


As illustrated in the Fibonacci example, a condition is expressed using the
ifTrue:ifFalse: message. Obviously, it expects to have a boolean as receiver.
This message takes two blocks as argument, the first one is evaluated in case
the boolean receiver is true, or the second block is evaluated in case the re-
ceiver is false. Variant exists such as ifTrue: and ifFalse:. For example, true
ifTrue: [ 5 ] evaluates to 5. The receiver can naturally be a combination of
boolean expression such as (5 < 1) ifFalse: [ ’5 is not less than 1’ ].

3.9 Collection
A Collection is a very common data structure. As previously illustrated, the
expression #(23 42 51) defines an array, instance of the class Array. This class,
and its superclasses, defines a large number of methods. Two operations are
very common in Pharo: transformation and filtering of collections.
A transformation is typically realized using collect:. For example, #(23 42
51) collect: [ :v | v > 30 ] returns #(false true true). The initial array of numbers
is transformed as an array of booleans.
Filtering is carried out using select:. For example, #(23 42 51) select: [ :v | v
> 30 ] returns #(42 51).
Both collect: and select: takes a block as argument. In case of select:, the
block has to evaluate to a boolean.
Collections in Pharo are rooted into the Smalltalk programming lan-
guage, and is often an inspiration for other programming languages. Pharo’s
Cascade 39

collections are rich and expressive. We have just seen the example of Ar-
ray. Another collection is OrderedCollection representing an expandable collec-
tions. Elements may be added and removed during the program execution.
For example:
v := OrderedCollection new.
v add: 23.
v add: 42.
v add: 51.
elements := (RTBox new size: #yourself) elementsOn: v.
RTVerticalLineLayout on: elements.
RTView new
addAll: elements;
yourself

This small script shows three squares.


Another useful collection is Dictionary. A dictionary stores pairs of keys
and values. For example, consider the following code snippet:
d := Dictionary new.
d at: #one put: 1.
d at: #two put: 2.
d at: #three put: 3.

The expression d at: #two returns the value 2.

3.10 Cascade
The last bit of syntax is yet to be described. A cascade allows one to send
several messages to the same object receiver. For example, instead of writing:
v := OrderedCollection new.
v add: 23.
v add: 42.
v add: 51.

One could write:


v := OrderedCollection new.
v
add: 23;
add: 42;
add: 51.

The cascade, noted ;, is a syntactic construction to make code more con-


cise by avoiding text duplication. It is frequently used in this book.
40 Pharo In a Nutshell

3.11 A bit of Meta-programming


Pharo provides an expressive reflective API, which means one can program-
matically get data about how Pharo code is structured and defined. Consider
the following expression RTShape methods size. This expression returns the
number of methods that the class RTShape defines. The message methods is
sent to the class RTShape, which is also an object in Pharo. This message
returns a collection of the methods defined on the class RTShape.
Many examples contained in Agile Visualization visualize software
source code and therefore use the reflective API. Source code is convenient
to illustrates visualization because it is already available (no need to rely on
external data) and is complex enough to deserve to be visualized.

3.12 Summary and Further Reading


This chapter gave a brief introduction to object-oriented programming.
From now on, you should be able to understand Pharo syntax. We recom-
mend a number of books to further discover the World of Pharo:

• Pharo by Example: http://pharobyexample.org

• Deep Into Pharo: http://deepintopharo.com

Pharo is a beautiful, elegant, and simple language. Pharo has a small and
concise syntax, which makes it each to learn. Its programming environment
is also highly customizable.
Building a sophisticated visualization or any non-trivial software artifact
often face complex development. Mastering object-orientation is not strictly
necessary in order to use Roassal. However, having a good command of
object-oriented programming will considerably alleviate development and
maintenance effort.
Pharo offers a powerful meta architecture. Do you remember that an ob-
ject is created by sending the message new to a class? In Pharo a class is
also an object since we send new to it, as in the expression Color new. A class
is therefore an object, itself an instance of another class, called a metaclass.
And it does not stop here. A metaclass is also an object. Methods are also
objects, a collection of bytecodes. Many parts of Pharo are truly beautiful,
but going into more detail is out of the scope of this book.
Chapter 4

Painting with Trachel

Any sophisticated visualization boils down primitives graphical elements.


Ultimately, no matter how complex a visualization is, circles, boxes, labels,
lines are the bricks to convey information.
Before jumping into Roassal, it is important to get a basic understanding
on using primitive graphical elements. Trachel is a low-level API to draw
primitive graphical elements. This chapter briefly describes Trachel.

4.1 Core of Trachel


Trachel is composed of the following components:

• Canvas: a container of shapes. Adding a shape to the canvas makes it


visible and responsive to user events.
• Shape: a visual entity, such as colored line, circle, box, and text.
• Event: a user action typically involving mouse and keyboard.
• Camera: a model that describes the visible portion of the canvas. A
camera has a position and a scaling factor.
• Viva: infrastructure to animate shapes.

4.2 Using the Canvas


A canvas is a set of graphical shapes that may react to events and may be
shown using the camera. As the first example, open a playground from the
World menu and enter the code:
42 Painting with Trachel

Figure 4.1: Instantiating canvas and a shape.

c := TRCanvas new.
c addShape: (TRBoxShape new size: 40).
c

Execute the code by pressing the Do it all and go button (the same option
is accessible from right clicking on the whole selection, or pressing Cmd-G
or Ctrl-G). Figure 4.1 shows what you are currently seeing.
A canvas is simply created by instantiating the class TRCanvas. A shape
is added to the canvas by sending the message addShape: with a shape as
argument to a canvas. In the example given above, the TRBoxShape describes
a box which has a size of 40 pixels. Since we have not specified a color of a
shape, gray is used. Gray is the default color for most shapes.
A shape may be removed from a canvas by simply sending the remove
message to shape.

c := TRCanvas new.
shape := TRBoxShape new size: 40.
c addShape: shape.
shape when: TRMouseClick do: [ :event | event shape remove. c signalUpdate ].
c

On the example given above, the shape is removed from the canvas by
clicking on it. This example uses events, which will be described below. The
message signalUpdate is sent to the canvas to refresh the window. This mes-
sage has to be sent whenever the canvas is modified after being opened. The
message addShape: does not need to be followed by signalUpdate since the
canvas is opened after the script execution.
Adding shapes 43

Figure 4.2: Random size and colors.

4.3 Adding shapes


Size and colors of shapes is defined by sending size: and color: to a shape.
Consider the following example:
c := TRCanvas new.
100 timesRepeat: [
| shape |
shape := TREllipseShape new.
shape size: 50 atRandom.
shape color: Color random.
shape translateTo: (500 atRandom @ 500 atRandom) - (250 @ 250).
c addShape: shape ].
c

Consider the script given in Figure 4.2.


Several shapes are available. The most commonly used are TRBoxShape,
TREllipseShape, TRArcShape, TRBezierShape, TRBitmapShape, TRLineShape,
TRPolygonShape, TRSVGPath.
In addition, a new shape may be easily defined. This is useful in case you
need to answer a particular need that cannot be easily fulfilled otherwise.
All Trachel shapes answer the following messages:

• translateTo: to move a shape to a new position

• translateBy: to move a shape using a step, specified as a point

• topPosition, leftPosition, bottomPosition, rightPosition returns the position of


a shape taken from a particular side.
44 Painting with Trachel

• topPosition:, leftPosition:, bottomPosition:, rightPosition: set the position


based on a given position and a side of the shape. Note that these
methods may be invoked only on boxes and ellipses.

• extent:, extent to set and query the dimension of the shape. The result
of extent is a point for which the x component represents the width and
the y the height.

Most of the methods listed above requires a point as argument. Each tra-
chel shape is described by a matrix underneath. This affine matrix contains
the position, scale factors and rotations values.
The color of a shape may be set using colors:. The border color using
strokePaint: and the border width using strokeWidth:.

4.4 Fixed Shapes


Shapes visible in a canvas depends on the position and altitude of the cam-
era. Some shapes may not be subject to the camera: some objects may remain
fixed even if the camera moves. This is the case for a menu button for exam-
ple. Consider the following script (Figure 4.3):
c := TRCanvas new.

100 timesRepeat: [
shape := TREllipseShape new.
shape
color: Color random;
size: 30 atRandom;
translateTo: (400 atRandom @ 400 atRandom).
c addShape: shape ].

button := TRLabelShape new text: 'move'.


c addShape: button.
button setAsFixed.
button translateBy: 30 @ 30.

button when: TRMouseClick do: [ :evt |


c camera translateBy: 4 atRandom @ 4 atRandom.
c signalUpdate ].
c

The code given above creates a new canvas, and adds one hundred el-
lipses, each having a random size, color, and position. The variable button
holds a label. By sending the message setAsFixed to it, the label remains lo-
cated on the top left corner. Clicking on it slightly moves the camera. All
Scaling and rotating 45

Figure 4.3: Fixed shape.

the circles moves accordingly. The label button remains at the same position,
since it is fixed.

4.5 Scaling and rotating

Several operations may be performed on a shape.

• scaleBy: makes the elements scale.

• rotateByDegrees: incrementally rotates the shape

• rotateToDegrees: set the rotation of a shape

4.6 Callback and event handling

A callback refers to a piece of code that is trigged from an event. Such events
are typically mouse movements, mouse clicks or keyboard strokes. Call-
backs are registered using the message when:do:. The first argument is an
event class (subclass of TREvent). Several events classes are available to cover
common user actions. The example given above associates a callback to the
object button: the block provided as second argument is evaluated when click-
ing on the Trachel shape.
46 Painting with Trachel

4.7 Roassal generates Trachel shapes


Trachel is a simple object model on top of which Roassal is built. In prac-
tice, Trachel is rarely directly used to visualize data. Instead, Roassal offers
expressive operations that create and manipulate Trachel shapes.
A visualization is updated by directly modifying Trachel shapes. Most
Roassal objects are used when building the visualization. Once the visual-
ization is built and displayed, then Roassal objects are pretty much useless
if no interaction is embedded.
Chapter 5

Visualizing with Roassal

Roassal maps objects and connections to graphical elements and edges. Val-
ues and metrics are mapped to visual dimensions, such as the width, height,
or the color intensity. Mapping objects to visual attributes is an expressive
way to build flexible and rich visualizations, and easily define this mapping
significantly reduce the effort to build a visualization. This chapter gives
an overview of Roassal and its main API. It covers the essential concepts of
Roassal, including the view, elements, shapes, and interactions.

5.1 View, Elements, Shapes and Interactions

Roassal structures a visualization in terms of views, elements, shapes, inter-


actions, and animations. A view is a container of graphical elements. Ele-
ments can be added and removed from a view. Most visualization contains
one single view that contains all the Roassal elements.
An element is a graphical representation of an object, typically describing
an arbitrary domain (e.g., information about seism, a file stored on the hard
disk, a tweet). An end-user sees elements and interacts with elements using
the mouse and keyboard. An element is described by a shape that define its
visual representation. A shape describes a primitive visual representation
such as a box, a circle, a line or a label. Shapes may be combined to form
elaborated shapes. A Roassal element is a compound object that contains (i)
a two dimensional spatial location; (ii) a set of interactions; (iii) a combina-
tion of shapes; (iv) an object model, which is an arbitrary object that belongs
to the domain model provided by the user. To be visible, elements must have
at least one shape.
An interaction is a particular action the user may trigger. The range of
supported interactions is broad: dragging elements, popping up an element
48 Visualizing with Roassal

Figure 5.1: Linking numbers.

when locating the mouse above, highlighting other elements, getting a menu
by right-clicking on an element are all common interactions.
The following example renders a tree, where each node is a number (Fig-
ure 5.1):
v := RTView new.
shape := RTBox new color: Color blue trans.
elements := shape elementsOn: (1 to: 50).
v addAll: elements.
elements @ RTPopup.

RTEdgeBuilder new
view: v;
objects: (1 to: 50);
connectFrom: [ :i | i // 3 ].

RTTreeLayout on: elements.


v

The first line creates a new view as an instance of the class RTView and as-
sign it to the variable v. A colored box shape is then created. The expression
Color blue trans defines a transparent blue color. The shape is then used as
a factory of Roassal elements using the message elementsOn:. This message
takes a collection of object model as argument, and returns a collection of
RTElement. Each element has a numerical value, between 1 and 50 as object
model. These elements are then added into the view v. The expression @
RTPopup makes each element react when the mouse is above it: a popup is
displayed that shows the model of the element, the number in our case.
The relations between the elements are then expressed using the class RT-
EdgeBuilder. An instance of RTEdgeBuilder is created and then parametrized
with the view. This edge builder retrieves particular elements from the view
and draws edges between two elements. The code specifies that for each
number i between 1 and 50, an edge is drawn between i and i // 3. The
message // refers to the integer quotient: 10 // 4 = 2.
Computing shapes dimensions 49

You can know more about this message by writing // in a playground,


selecting these two characters with the mouse, right-clicking and selecting
"implementors of it". This opens a new window showing all the methods in
the system named //. Comments contained in the code describe the purpose
of the method.

5.2 Computing shapes dimensions


One key aspect of Roassal is that shapes may hold metrics or numerical
values that define their visual dimensions (e.g., height, width, color, border
width). A shape may take absolute numerical values (e.g., RTBox new width:
30; height: 40) or some metric definitions (e.g., RTBox new width: [ :cls | cls
numberOfVariables ]; height: [ :cls | cls numberOfMethods ]).
The following code is a more elaborated example that extracts data from
a Tab Separated Values (TSV) table (Figure 5.2):
"Processing a small TSV table"
tab := RTTabTable new input:
'id value1 value2 category parent
1 10 20 A 1
2 5 12 B 1
3 8 17 A 1
4 9 13 D 3
5 30 30 D 3'.
tab removeFirstRow.
"Converting columns into integer"
tab convertColumnsAsInteger: #(1 2 3 5).

v := RTView new.
colorNormalizer := RTMultiLinearColorForIdentity new
objects: (tab values collect: #fourth) asSet sorted;
command: #fourth.
shape := RTBox new
width: #second;
height: #third;
color: colorNormalizer.
elements := shape elementsOn: tab values.
v addAll: elements.

elements @ (RTPopup new text: [ :entry |


'id = ', entry first asString, String cr,
'value1 = ', entry second asString, String cr,
'value2 = ', entry third asString, String cr ]).

elements @ (RTLabeled new text: [ :entry | entry first ]).


50 Visualizing with Roassal

Figure 5.2: Linking TSV elements.

RTEdgeBuilder new
view: v;
objects: tab values;
connectFrom: [ :entry | tab values at: entry fifth ].

RTHorizontalTreeLayout new
verticalGap: 30; on: elements.
v

The script above extracts and visualizes data from a TSV table (any
spreadsheet application generate documents in that format). In this exam-
ple, we assume that each row contains a numerical identifier, two values,
and the identifier of a parent table entry. The class RTTabTable converts the
textual description of the table (i.e., the raw file content) into actual values
that are usable by Roassal. The first row of the table is removed since it sim-
ply names the columns and these names are not meaningful for our example.
The four columns are then converted into integer numerical values.
Since each table entry contains two values, we define a graphical box for
which the width and the height are directly mapped to the first and second
value, respectively. A popup is then defined that gives more details about the
table entry. This is followed by labeling each element with the first numerical
entry value, the identifier.
Lastly, edges are built, linking each entry to its parent, and all elements
are laid out as a horizontal tree. Note that we assume here that (i) entries
are ordered in the table according to their identifier and (ii) the parent of the
first entry is the entry itself.
A graphical element acts as a visual facade for the object that the element
represents, i.e. models provided using elementOn: or elementsOn:. Roassal
transparently associates each element to this model object. Typically these
objects are externally provided. For example, in the example with the small
table given above, each Roassal element represents an entry from the table.
Each element has a particular size that is computed from the table entry.
Shapes 51

The visual representation given by an element’s shapes and the interac-


tions offered to the end user depends on the model object. In Figure 5.2, all
the elements have the same shape: a RTBox object with two functions used
to compute the width and the height of an element. These functions are
evaluated against the table entry given as a parameter when the element is
created, using elementsOn:.

5.3 Shapes
In Roassal, several primitive shapes are offered, both for individual elements
and for edges.

Element shapes
Six shapes are meant to cover most typical usages:

• RTBox: a rectangular box, e.g., RTBox new width: 10; height: 20

• RTEllipse: an ellipse, e.g., RTEllipse new width: 10; height: 20

• RTLabel: a textual label, e.g., RTLabel new text: ’hello world’. The text may
be multi-lined.

• RTPolygon: a polygon, e.g., RTPolygon new vertices: { 25 @ 0 . 10 @ 50 .


50 @ 20 }

• RTBitmap: an image., e.g., RTBitmap new form: RTIcon roassal

• RTSVGPath: an SVG path, e.g., RTSVGPath new path: ’M150 0 L75 200
L225 200 Z’

• RTArc: arc portion, e.g., RTArc new externalRadius: 100; innerRadius: 20;
outerAngle: 45 innerAngle: 90

For each of the examples above, filling in the ... in the following code
template produces an executable code snippet:
v := RTView new.
shape := ... .
v add: shape element.
v

An element shape understands the following messages, amongst others:

• size: sets both width and height to the provided argument


52 Visualizing with Roassal

• width: and height: set the width and the height, respectively

• color: sets the fill color of the shape

• borderColor: sets the border color of the shape

The complete set of messages that may be used with element shapes is
declared in the classes RTShape and RTAbstractElementShape. As already men-
tioned, these methods may accept numerical values, or block functions. In
case a block is provided, it has to return a numerical value when evaluated
against the object model.

Edge shapes
Edges are typically drawn between two elements. Similar to elements, an
edge may have a shape.

• RTLine: a straight line between its extremities, e.g., RTLine new color:
Color blue

• RTArrowedLine: an arrowed line between its extremities, e.g., RTArrowed-


Line new color: Color blue

• RTDirectedLine: a directed line, e.g., RTDirectedLine new. The directed


line is a kind of Bezier line for which the curve indicates the direction.

• RTMultiLine: segmented lines, e.g., RTMultiLine new orthoVertical; color:


Color blue

The following template shows the use of edge shapes. As previously,


simply replace the missing part with the shape variable by an expression
example given above.
v := RTView new.

elementShape := RTEllipse new size: 20; color: Color blue trans.


e1 := elementShape elementOn: 'Start'.
e2 := elementShape elementOn: 'End'.
v add: e1.
v add: e2.
e1 @ RTDraggable.
e2 @ RTDraggable.
e1 @ RTLabeled.
e2 @ RTLabeled.
e2 translateTo: 50 @ 100.

shape := ... .
Shapes 53

Figure 5.3: Example of labeling a box.

v add: (shape edgeFrom: e1 to: e2).


v

The expression e1 @ RTDraggable makes the element e1 draggable using


the mouse. The expression e1 @ RTLabeled adds a label above the element
e1. This label reflects the object model of e1, the string object ’Start’ in our
case.

Shape composition
In some cases, primitive graphical elements are simply not enough. One
tipical scenario is labeling, often achieved by combining a label and a graph-
ical element. Roassal offers a simple and expressive mechanism to compose
shapes in order to produce more elaborated ones (such as a labeled rectangle)
Consider the following example (Figure 5.3):
v := RTView new.

s := (RTBox new size: 30) + RTLabel.


es := s elementsOn: (1 to: 20).
v addAll: es.
RTGridLayout on: es.
v

A shape may receive the message + to be composed with another shape.


The expression (RTBox new size: 30) + RTLabel produces a new shape, a 30-
pixeled-size box with a label in it. The value of the label is provided by
the model object behind the associated element: a numerical value, ranging
from 1 to 20, in our case.
The method + is defined on the class RTShape a:
RTShape >> + anotherShape
^ RTCompositeShape with: self with: anotherShape instance
54 Visualizing with Roassal

Figure 5.4: Labeling a box of a particular size.

Underneath, the class RTCompositeShape is used for composition. The


expression (RTBox new size: 30) + RTLabel has therefore the same effect than
RTCompositeShape with: (RTBox new size: 30) with: RTLabel new.
The class RTCompositeShape provides a number of useful methods to
structure the composite shapes. For example, in case you wish to have the
label below the box and have a different size for each box, you can use the
following (Figure 5.4):
v := RTView new.

s := RTCompositeShape with: (RTBox new size: #yourself) with: RTLabel new.


s vertical.
es := s elementsOn: (10 to: 100 by: 5).
v addAll: es.
RTFlowLayout on: es.
v

The script above creates a composite shape made of two shapes: a box
and a label. The use of the vertical message sent to this composite shape
makes the first subshape (i.e., the box) above the second subshape (i.e., the
label).
Composed shapes are often employed to give a title to elements. The
following example visualizes some of the classes defined in Roassal (Figure
5.5):
v := RTView new.
shape := RTCompositeShape new.
shape add: (RTLabel new color: Color gray).
shape add: (RTBox new
color: Color lightRed;
width: [ :cls | cls numberOfVariables * 8 ];
Shapes 55

Figure 5.5: Giving a title to some boxes.

height: #numberOfMethods).
shape vertical.
es := shape elementsOn: RTShape withAllSubclasses.

v addAll: es.
RTGridLayout on: es.
v

The expression RTShape withAllSubclasses returns a collection of Pharo


classes describing some Roassal shapes: the script above visualizes the Roas-
sal application in Roassal itself! Each class is represented as a composite
shape. A title is first added to the composite shape. Then a box is added,
its width represents the number of variables and the height represents the
number of methods defined in each class.
We have previously seen that the interaction RTLabeled can be used to
give a label to an element. The natural question is how does RTLabeled differ
from using a composite shape? The answer is that using RTLabeled add a
label to the view, and makes it stick to the element. The size of the element
is defined by the shape it originates from. This matters when doing a layout.
Consider the following example (Figure 5.6):
v := RTView new.
shape := RTBox new
color: Color lightRed;
width: [ :cls | cls numberOfVariables * 8 ];
height: #numberOfMethods.
es := shape elementsOn: RTShape withAllSubclasses.

v addAll: es.
es @ RTLabeled.
RTGridLayout on: es.
v

This example is similar to the composite shape example given previously.


Instead of using a composite shape, the interaction RTLabeled is used. The
result is immediately visible: the labels overlap when doing a layout. Note
that this behavior may be wished in some case. An interesting configuration
option on RTLabeled is setAsHighlightable. Consider this slightly improved
56 Visualizing with Roassal

Figure 5.6: Giving a title to some boxes using RTLabeled.

Figure 5.7: Giving a title to some boxes using RTLabeled and highlight.

version (Figure 5.7):


v := RTView new.
shape := RTBox new
color: Color lightRed;
width: [ :cls | cls numberOfVariables * 8 ];
height: #numberOfMethods.
es := shape elementsOn: RTShape withAllSubclasses.

v addAll: es.
es @ (RTLabeled new setAsHighlightable) .
RTGridLayout on: es.
v

The interaction we use is RTLabeled new setAsHighlightable: when the


mouse goes above an element, the label is highlighted, thus making it read-
able.
Composite shapes may have a layout to order the inner shapes. Consider
the following example:
s := RTCompositeShape new.
b1 := RTBox new color: Color green.
b2 := RTBox new color: Color blue; size: 20.
b3 := RTLabel new.
s add: b1.
s add: b2.
s add: b3.
s horizontal.
s setAutomaticRelayout.

e := s elementOn: 'click to expand'.


view := RTView new.
view add: e.
Shapes 57

Figure 5.8: Composing shapes.

e @ RTDraggable.
e @ RTPopup.

e when: TRMouseClick do: [ :ev |


e trachelShape shapes second extent: 45 @ 45.
e trachelShape shapes third text: 'hello'.
view signalUpdate.
].

view open

Clicking on the element resizes the second and third inner shapes. Since
the automatic relayout has been set using #setAutomaticRelayout, shapes will
be properly ordered.
Shapes may also be translated within a composite shape. Consider the
following example that paints four national European flags (Figure 5.8):

view := RTView new.


shape := RTCompositeShape new.
shape add: (RTBox new color: #first; height: 20; width: 15).
shape add: (RTBox new color: #second; height: 20; width: 15) translateBy: 15 @
0.
shape add: (RTBox new color: #third; height: 20; width: 15) translateBy: 30 @ 0.

flags := Dictionary new.


flags at: 'France' put: (Array with: Color blue with: Color white with: Color red).
flags at: 'Belgium' put: (Array with: Color black with: Color yellow with: Color red).
flags at: 'Romania' put: (Array with: Color blue with: Color yellow with: Color red).
flags at: 'Italia' put: (Array with: Color green with: Color white with: Color red).

els := shape elementsOn: flags values.


view addAll: els.

RTGridLayout new on: els.


view
58 Visualizing with Roassal

Figure 5.9: Element rotation.

5.4 Element transformation


An element may be translated in the view using the translateBy: and trans-
lateTo: messages, both taking a point as argument.
Elements may be rotated by sending rotateByDegrees:. This message ac-
cepts as argument a numerical value (e.g., rotateByDegrees: 30), a symbol or
a block, as in the following example (Figure 5.9):
v := RTView new.
shape := RTBox new width: 5; height: 25.
es := shape elementsOn: (1 to: 90 by: 5).
es rotateByDegrees: #yourself.
RTHorizontalLineLayout new gapSize: 2; on: es.
v addAll: es.
v

In total, 18 elements are added in the view. Each element represents a


value between 1 and 90, with an interval of 5. The variable es represents a
group of elements. By sending the message rotateByDegrees: to a group, each
element of the group is rotated as specified in the argument of the message.

5.5 Group of elements


The class RTGroup is an ordered collection to contain elements. A group is a
composite of elements and dispatches each received messages to each con-
tained element received messages. A group may be resized, rotated, and
scaled.
Groups are used to uniformly manipulate a set of elements. In the exam-
ple given above, the expression shape elementsOn: (1 to: 90 by: 5) returns a
group, to which the message rotateByDegrees: #yourself is sent. Note that a
group may also contain another group.

5.6 Nesting elements


Expressing containment is critical as soon as the data to be represented is hi-
erarchical. Being able to embed elements into other elements is a natural way
Nesting elements 59

Figure 5.10: Nesting boxes into a larger box.

to express encapsulation. Roassal offers several options to express nesting or


encapsulation, all rooted in the class RTNest.
The message on: element nest: someElements is probably the most com-
monly used. This message nests someElements into element. Consider the
following example (Figure 5.10):
v := RTView new.
el := (RTBox new width: 80; height: 40; color: (Color purple alpha: 0.3)) element.
shape := RTBox new color: (Color red alpha: 0.3); size: #yourself.

innerElements := (1 to: 30) collect: [ :i | shape elementOn: i ].


v addAll: innerElements.

RTGridLayout new on: innerElements.


RTNest new
on: el nest: innerElements.
v add: el.
v

The message on:nest: takes as the first argument an element, onto which
you wish to nest a group of elements provided as second argument. The nest-
ing element (i.e., the first argument provided to on:nest:, el in our example)
is resized to encapsulate the nested elements. Dragging an encapsulating
element also drag its child elements.
The precedent example first sets the layout for the inner elements, then
nests the elements (RTGridLayout on: inner. RTNest new on: el nest: inner). A
layout may be specified when using RTNest. This may be written as follows:
RTNest new
layout: RTGridLayout new;
on: el nest: innerElements.

The message for:add: is useful for iterating over a group of elements and
defines nested elements for each element of a set. Consider the example
(Figure 5.11):
v := RTView new.
60 Visualizing with Roassal

Figure 5.11: Using for:add: to nest elements.

es := (RTBox new color: Color blue trans)


elementsOn: (1 to: 20).
v addAll: es.

RTNest new for: es add: [ :group :model |


group addAll: ((RTBox new color: Color red) elementsOn: (1 to: model)).
RTGridLayout on: group ].

RTGridLayout on: es.


v

The message for:add: takes as first argument the group of elements to


iterate over. The second argument is a block that follows the pattern [ :group
:model | ... ]. The variable group is a group in which elements to be nested have
to be added. The variable model is the model represented by the nesting node.
The model is often essential to build the elements that have to be nested.
As a further example, consider the following script:
v := RTView new.
es := (RTBox new color: Color white; borderColor: Color lightGray)
elementsOn: { RTLayout . RTShape . RTBuilder }.
v addAll: es.
es @ RTDraggable.

RTNest new for: es add: [ :group :model |


elements := (RTBox new color: Color red) elementsOn: model
withAllSubclasses.
group addAll: elements.
edges := RTEdgeBuilder new
view: group;
objects: model withAllSubclasses;
connectFrom: #superclass.
RTTreeLayout on: elements edges: edges .
].

RTGridLayout on: es.


Interaction to empower elements 61

Figure 5.12: Using for:add: and edges.

Figure 5.12 illustrates the usage of for:add: in which edges are added in
the group.

5.7 Interaction to empower elements


In Roassal, an interaction is modeled as a particular object that augments a
Roassal element with appropriate answers to user event. A large range of
user events are supported: mouse click, move movement and key strokes
are probably the most common user events. Interaction may be set to a view,
elements or edges.
Three interactions may be set to a view:

• RTDraggableView to set a view draggable. E.g., view @ RTDraggableView.

• RTHorizontalDraggableView to make a view horizontally draggable.

• RTVerticalDraggableView to make a view vertically draggable.

• RTDoubleScrollBar to add scrollbars in a view.

A number of interactions are available for elements:


62 Visualizing with Roassal

Figure 5.13: Dragging connected elements.

• RTDraggable makes an element draggable. A user can then use the


mouse to drag and drop the element.
• RTDraggableChildren makes an element draggable. When being
dragged, connected elements with an edge are also dragged. Consider
the following example that uses RTDraggableView and RTDraggableChil-
dren (Figure 5.13 has been obtained by dragging the number 5):

v := RTView new.
v @ RTDraggableView.

es := RTLabel elementsOn: (1 to: 40).


v addAll: es.
RTEdgeBuilder new
view: v;
elements: es;
connectFrom: [ :aValue | aValue // 2 ].
RTClusterLayout on: es.
es @ RTDraggableChildren.
v

• RTPopup dynamically adds a text to the element pointed by the mouse.


• RTLabeled adds a label above an element. The label may be particu-
larized using text: if the default string representation is not sufficient.
Consider:
Interaction to empower elements 63

v := RTView new.
e := (RTEllipse new size: 30) elementOn: 42.
v add: e.
e @ (RTLabeled new text: [ :value | 'My value is ', value asString ]).
v

• RTShowLabel adds a label on a set of elements when the mouse enters a


particular element. RTLabeled is used for the labeling. When the mouse
leaves the element, all the labels are removed.

• RTShowEdge adds edges on one particular element when the mouse


is above the element. Edges are removed when the mouse leaves the
element.

• RTSetEdgeAlpha temporarily decreases the transparency of the incom-


ing and outgoing edges for a given element when the mouse enters
the element. When added to an element, connected edges are made
transparent.

• RTResizeable enables to resize en element by the user. Consider the


following example:

v := RTView new.

s := RTBox new color: Color blue; size: 20.


e := s element.

e @ RTResizable.
v add: e.
v

Event may be propagated to other elements using RTEventForwarder. This


is handy in case objects have to forward some particular actions. Consider
the following example
v := RTView new.

box1 := (RTBox new size: 20) element.


box2 := (RTBox new size: 20) elementOn: 'hello'.

box2 translateBy: 50 @ 0.
box2 @ RTDraggable.

box1 @ RTDraggable.
box1 @ (RTEventForwarder new toObject: box2).
64 Visualizing with Roassal

v add: box1.
v add: box2.
v

Dragging the left box forward the dragging events to the right box. An-
other example of RTEventForwarder is in case elements are above other ele-
ments. Consider the following example:
v := RTView new.

inner := (RTBox new color: Color green; size: 30) elementOn: 'world'.
outer := (RTBox new color: Color blue) elementOn: 'hello'.

inner @ (RTHighlightable new highlightedColor: Color yellow).


outer @ RTPopup.
inner @ (RTEventForwarder new toObject: outer).

v add: outer ; add: inner.


RTNest new on: outer nest: { inner }.
v

The element inner, located above outer, had an interaction RTHighlightable.


Without this interaction, locating the mouse above inner displays the popup
for outer. However, since inner has an interaction already, the elements an-
swer events RTMouseLeave and RTMouseEnter. The runtime does not search
for elements answering these events which may be located below it. RTEvent-
Forwarder is here used to make inner answer the highlighting interaction and
triggering the popup of outer.

5.8 Composite shapes vs RTLabeled


We introduced the notion to compose shapes as an effective way to label
elements. Why do we then need the interaction RTLabeled if one can simply
write RTBox + RTLabel? For example, the following example (Figure 5.14):
v := RTView new.

cShape := RTCompositeShape new.


cShape add: (RTLabel new text: [ :aValue | 'Value = ', aValue asString ]).
cShape add: (RTBox new size: 30).
cShape vertical.

es := cShape elementsOn: (1 to: 20).


es @ RTDraggable.
v addAll: es.
RTGridLayout on: es.
Composite shapes vs RTLabeled 65

Figure 5.14: Composing shapes with a label.

The code above creates a composite shape with two shapes, a label and a
box. Elements are then built from the composite shape.
Using the RTLabeled interaction, a similar code may be (Figure 5.15):
v := RTView new.

s := (RTBox new size: 30).


es := s elementsOn: (1 to: 20).
es @ RTDraggable.
v addAll: es.

es @ (RTLabeled above text: [ :aValue | 'Value = ', aValue asString ]).


RTGridLayout on: es.
v

After having created elements, a label is added to the elements.


There is a number of differences between using a composite shapes and
using RTLabeled:

• The label added with RTLabeled is not taken into account when com-
puting the encompassing boundary of the element. As a result, the
layout does not take the label into account, which may result in over-
lapping labels, as Figure 5.15 illustrates. There is no general rule on
whether the label has to be taken into account when layouting. It all
depends on the purpose of the visualization.
66 Visualizing with Roassal

Figure 5.15: Layout and RTLabeled.

• RTLabeled may be selectively applied to some elements, while with a


composite shape, all the elements produced by the shape have a label.

5.9 Normalization
Being able to quickly compare data elements is essential in a visualization.
Pre-attentive processing refers to the ability of the human brain to uncon-
sciously accumulate information from the surroundings. Without even re-
alizing what is in front of our eyes, the brain filters and processes what is
important. This phenomena is particular relevant in data visualization. Our
brain is able to immediately spot an element that is different shaped or col-
ored.
The class RTMetricNormalizer normalizes one or more visual dimensions
of a set of Roassal elements. Consider the following example (Figure 5.16):
v := RTView new.
es := (RTEllipse new color: Color blue) elementsOn: #(4 5 1 2 3 5).
v addAll: es.

es @ RTPopup.

RTMetricNormalizer new
elements: es;
alphaColor;
normalizeSize: #yourself.

RTHorizontalLineLayout new alignCenter; on: es.

es @ RTLabeled.

v
Normalization 67

Figure 5.16: Normalizing element size.

The script above creates six elements, each representing one of the values
in #(4 5 1 2 3 5). Elements are then added to the view and a popup interaction
is given to each element.
The class RTMetricNormalizer is then instantiated. Elements to be normal-
ized are set in the metric normalizer using elements:. Elements are first made
translucent (note that strictly speaking the message alphaColor is not a nor-
malization, but rather a convenience). The message normalizeSize: normal-
izes the size of each element against the set of elements provided with ele-
ments:. The default minimum size is 5 pixels and the maximum is 30 pixels.
Such values may be specified using normalizeSize:min:max:. For example:

v := RTView new.
es := (RTEllipse new color: Color blue) elementsOn: #(4 5 1 2 3 5).
v addAll: es.

es @ RTPopup.

RTMetricNormalizer new
elements: es;
alphaColor;
normalizeSize: #yourself min: 20 max: 50;
normalizeColor: #yourself.

RTHorizontalLineLayout new alignCenter; on: es.

es @ RTLabeled.
v

The example given above normalizes both the size and the color of the
elements. The color palette is continuous, ranging from gray to red. Colors
68 Visualizing with Roassal

Figure 5.17: Normalizing element size and color.

may be provided using normalizeColor:using:, as in the instruction normalize-


Color: #yourself using: { Color gray . Color orange . Color black }.
Similarly, the position along the X-axis may be normalized. Consider the
example below (Figure 5.18):
v := RTView new.
es := (RTEllipse new size: 15; color: Color blue) elementsOn: #(4 5 1 2 3 5).
v addAll: es.

es @ RTPopup.

RTVerticalLineLayout new alignCenter; on: es.

RTMetricNormalizer new
elements: es;
alphaColor: 0.3;
normalizeX: #yourself min: 0 max: 30.

es @ RTLabeled.

Normalization is essential for rendering curve and graph. Consider the


script:
v := RTView new.
es := (RTBox new size: 15; color: Color blue) elementsOn: #(4 5 1 2 3 5).
v addAll: es.

es @ RTPopup.

RTMetricNormalizer new
Normalization 69

Figure 5.18: Normalizing element position.

Figure 5.19: Normalizing element height.

elements: es;
alphaColor: 0.3;
normalizeHeight: #yourself min: 0 max: 80.

RTHorizontalLineLayout new alignBottom; on: es.

es @ RTLabeled.

You can notice this histogram is not quite right. The bar for the element
1 is not visible. This is because it has a height of 0. This is expected since
the minimum value of the elements model is 0 pixel (i.e., the value provided
to min:). A proper version of our (primitive) bar charting needs to specify a
70 Visualizing with Roassal

Figure 5.20: Normalizing element height.

minimum and maximum value. Consider (Figure 5.20):


v := RTView new.
es := (RTBox new size: 15; color: Color blue) elementsOn: #(4 5 1 2 3 5).
v addAll: es.

es @ RTPopup.

RTMetricNormalizer new
elements: es;
alphaColor: 0.3;
normalizeHeight: #yourself min: 0 max: 80 minValue: 0 maxValue: 5 .

RTHorizontalLineLayout new alignBottom; on: es.

es @ RTLabeled.

The message normalizeHeight:min:max:minValue:maxValue: takes as argu-


ment a minimum and maximum value.
The normalizer may also give a color to each element based on a particu-
lar attribute. Consider the following example (Figure 5.21):
v := RTView new.
es := RTBox elementsOn: Collection withAllSubclasses.
v addAll: es.
n := RTMetricNormalizer new
elements: es;
normalizeHeight: #numberOfMethods;
normalizeWidth: #numberOfVariables;
Expressing constraints 71

Figure 5.21: Normalizing element height, width, and color.

distinctColorUsing: #package.
RTHorizontalFlowLayout on: es.
v

The script above creates a boxed element for each subclasses of the class
Collection. These elements are then added in a view. The normalizer com-
putes the height using the number of methods, the width using the number
of variables, and gives a color to each class based on the package the class
belongs to.

5.10 Expressing constraints


A constraint is a relation between two or more elements about their position.

Alignment
Adequately positioning some elements against other elements is often cru-
cial. It frequently happens that an element has to be resized or positioned
against other elements or even the window. The class RTAlignment offers a
number of methods dedicated to constrain the size or the position of some
elements.
Consider the following example (Figure 5.22):
values := #(35 26 5 18 17 60 36 13 16 28 29 3 8 0 3 2 14
12 13 17 1 9 3 3 4 1 1 1 2 1 1 61).
v := RTView new.
n := RTMultiLinearColorForIdentity new objects: values.
72 Visualizing with Roassal

Figure 5.22: Use of alignment.

shape := RTBox new color: n; size: #yourself.


es := shape elementsOn: values.
v addAll: es.
RTHorizontalLineLayout new gapSize: 0; on: es.
RTAlignment new elements: es; top.
v

Figure 5.22 illustrates the different ways to line up elements using the
class RTAlignment. Elements can be lined up with their lowest point (bottom),
with the left-most position (left), and with the right-most position using right.
You may want to replace RTHorizontalLineLayout by RTVerticalLineLayout to try
left and right in the script given above.
Alignment may also be defined using a particular element. You simply
need to set that element using fixedElement:. You can then use the messages
top, left, right, bottom.
The class RTGroup is polymorphic to RTElement, which means that you
can provide a group of elements where an element is expected. Consider the
following example (Figure 5.23):
rootClasses := { RTShape . RTLayout }.

groups := rootClasses collect: [ :cls |


g := RTGroup new.
elements := (RTEllipse new size: 8; color: Color blue)
elementsOn: cls withAllSubclasses.
g addAll: elements.
edges := RTEdgeBuilder new
Expressing constraints 73

Figure 5.23: Group alignments.

view: g;
objects: cls withAllSubclasses ;
connectFrom: #superclass.
RTTreeLayout on: elements edges: edges.
g ].

v := RTView new.
groups do: [ :aGroup | v addAll: aGroup ].
RTHorizontalLineLayout new gapSize: 30; on: groups.

RTAlignment new elements: groups; bottom.


v

The variable named groups contains a collection of RTGroup, each contains


elements corresponding to all the subclasses of a particular class. The mes-
sage elements:, sent to the object RTAlignment, takes as argument the groups
variable.

Positioning
The class RTConstraint offers a number of methods to position elements
against other elements. A constraint has to be properly initialized using fix:
to set those that are fixed elements and movable: to set the elements that have
to be moved and/or constrained.
v := RTView new.

e := (RTBox new size: 50) element.


e @ RTDraggable.
v add: e.

l := RTLabel elementOn: 'This is a label!'.


v add: l.

RTConstraint new
74 Visualizing with Roassal

movable: l;
fix: e;
inner;
bottom;
move.
v

The code above simply moves the label at the bottom of the square, while
being in the inner side. You may experiment by replacing:

• inner by outer or border, to have the movable element on the outer or


border side of the fixed element.
• bottom by right, top, left. Note that position may be combined with left;
top for example.

• move by stick to make the label stick to the square when dragged and
dropped.

The move operation moves the movable element according to the fixed
element. Using the mouse to drag the box does not move the label. To do so,
you need to use the stick operation.
The example given above uses a single element for fix: and movable:.
Groups may be provided instead to handle a set of elements. Consider the
following example (Figure 5.24):
v := RTView new.
n := RTMultiLinearColorForIdentity new
numberOfColors: 20;
colors: { Color red . Color gray }.
es1 := (RTEllipse new color: n; size: 15) elementsOn: (1 to: 20).
v addAll: es1.

n := RTMultiLinearColorForIdentity new
numberOfColors: 20;
colors: { Color yellow . Color purple }.
es2 := (RTEllipse new color: n; size: 15) elementsOn: (1 to: 20).
v addAll: es2.

RTForceBasedLayout on: es1.


RTGridLayout on: es2.
RTConstraint new movable: es1; fix: es2; above; move.
v

Two groups of elements are used in this example, es1 and es2. A force
based layout is applied to the first group while a grid layout is applied to
the second group. The use of RTConstraint positions the first group above the
second group.
Shape and Edge builder 75

Figure 5.24: Positioning a group above another group.

5.11 Shape and Edge builder


Roassal offers a rich class library to define elements and edge shapes. These
shapes cover typical uses of Roassal. Roassal shapes are modeled with the
hierarchy rooted in RTShape. Directly Manipulating shapes is often tedious.
Instead, Roassal offers the shape and edge builder.

Element shapes
Consider the following example (Figure 5.25):
v := RTView new.
sb := RTShapeBuilder new box
height: #numberOfMethods;
width: [ :cls | cls numberOfVariables * 4 ];
withTextAbove.
es := sb elementsOn: RTShape withAllSubclasses.
v addAll: es.
RTFlowLayout on: es.
v

A shape builder is first created. It is then configured as a box (note that


box is synonym to rectangle). A height: and a width: is then provided to set
the height and the width of the boxes that will be produced later on. The
message withTextAbove sets the shape builder to give a label to the boxes. A
number of methods exist:

• withText augments the shape with a label, located at the center of it


• withTextAbove defines the shape with a text above
76 Visualizing with Roassal

Figure 5.25: Locating text above each box.

• withTextBelow makes the text located below.

A block may be provided to these methods to compute the text to be used


(withText:, withTextAbove:, withTextBelow). This example shows the benefits of
using the shape builder: there is no need to explicitly use composed shapes.
The builder does it for us.

Edge shapes
The edge builder create connections between elements and it is an important
asset for Roassal. The expressiveness of the Edge Builder is based on a semi-
declarative relation. Consider the following example (Figure 5.26):
v := RTView new.
shape := (RTEllipse new size: 20; color: Color red trans) + RTLabel.
es := shape elementsOn: (1 to: 20).
v addAll: es.

eb := RTEdgeBuilder new.
eb view: v.
eb shape orthoVerticalLine.
eb
objects: (1 to: 20);
connectFrom: [ :n | n // 3 ].

v elements @ RTDraggable @ RTPopup.

RTTreeLayout on: v elements.


v

An edge builder is obtained by first instantiating the class RTEdgeBuilder.


The view has to be provided since the purpose of the edge builder is to build
edges, which have to be added in a view. An edge builder offers a shape
builder to let the user define line shapes. The shape builder used by the
Dynamically update 77

Figure 5.26: Orthogonal lines obtained from a builder.

edge builder is obtained by sending the message #shape to the builder. In the
example above, we simply select orthogonal vertical lines using the message
#orthoVerticalLine.
Some objects may be specified to the edge builder. These objects define
the scope of how to build edges. The message #objects: sets the objects from
which the edges are starting and ending. In the example above, an edge is
built from each object ranging from 1 to 20.
The message #connectFrom: takes a bloc as parameter. In the example the
bloc is [ :n | n // 3 ]. The bloc accepts one argument and is evaluated for each
object. Evaluating the bloc returns a new number to which the edges will be
connected.
The class RTEdgeBuilder contains numerous methods that offers a great
flexibility on building edges. We advice you to browse the class to have the
complete list of methods. Methods that are common used are:

• connectFrom: builds an edge from the result of the bloc to the current
object.

• connectFrom:to: allows you to specify the ending object in addition to


the starting object.

• connectToAll: creates many lines: the argument bloc returns a list of


objects to which edges have to be build. Many edges can therefore be
built for one single object.

The chapter on Mondrian provides several examples using the edge


builder.

5.12 Dynamically update


Updating a visualization may considerably improve the user experience.
Roassal offers a number of facilities to add, update, and remove elements.
78 Visualizing with Roassal

Figure 5.27: Updating edges upon mouse cursor movement.

Updating edges
As soon as an element is moved (using translateBy: or translateTo:), edges
connected to it are updated. Consider the following example (Figure 5.27):
v := RTView new.
es := RTBox elementsOn: (1 to: 300).
RTGridLayout new gapSize: 30; on: es.

edges := OrderedCollection new.


es do: [ :e |
| te |
te := RTBox element.
te translateTo: e position + (0 @ 10).
edges add: ((RTArrowedLine new color: Color black) edgeFrom: e to: te).
].

v addAll: edges.

v when: TRMouseMove do: [ :evt |


edges do: [ :edge |
edge to
translateTo: (evt positionFromCamera - edge from position) / 10 + edge
from position.
].
v signalUpdate.
].
v

All the arrowed edges points towards the mouse cursor. The variable
edges is initialized with an empty collection. This collection is filled with
edges obtained from an arrowed line. Note that the elements contained in
es and e are not added in the view; only the edges are.
A callback is set to the view when the mouse cursor is moved: the to
extremity of the edge is translated accordingly. The message positionFrom-
Camera is used to get the mouse cursor position in the space coordinate.
Dynamically update 79

Dynamically adding elements

Elements may be easily added and removed. Consider the following exam-
ple (Figure 5.28):

v := RTView new.
v @ RTDraggableView.

"We set a dynamic spring layout"


stepping := RTSpringLayoutStepping new view: v.
stepping after: [ v canvas camera focusOnCenter ].
v addAnimation: stepping.

"Add a new circle when pressing on a button"


v canvas addMenu: 'Add circle' callback: [
| el |
el := (RTEllipse new size: 20; color: (Color blue alpha: 0.4)) element.
el @ RTDraggable.
el translateTo: 5 atRandom @ 5 atRandom.
v add: el.
stepping addNode: el.
v signalUpdate.
].

"Add a new edge when pressing on a button"


v canvas addMenu: 'Add connected circle' callback: [
| el edge |
el := (RTEllipse new size: 20; color: (Color blue alpha: 0.4)) element.
el @ RTDraggable.
el translateTo: 5 atRandom @ 5 atRandom.
v add: el.

edge := RTLine edgeFrom: el to: v elements atRandom.


v add: edge.

stepping addNode: el; addEdge: edge.


v signalUpdate.
].
v

The Trachel canvas offers facilities to add menu to the canvas. We use
the message addMenu:callback: to let the user to add nodes and edges. Most
of the script given above deals with the dynamic spring layout: adding an
element or an edge needs to update the layout.
80 Visualizing with Roassal

Figure 5.28: Dynamically adding elements.

5.13 Roassal visualization in a web browser


Roassal may export a visualization toward several file formats, including
PNG, SVG, HTML5. Exporting a visualization to HTML5 produces two files,
the HTML file and a file called roassal.js. The visualization is encoded in
JavaScript and makes use of the roassal.js JavaScript library. Some interac-
tions are preserved when being exported, in particular drag-and-drop and
popup.

5.14 Debugging a visualization


Debugging a visualization may be complicated in some situations. In case
an anomaly occurs during the construction of the view content, then an error
will popup a Pharo debugger. In case the view triggers an error during the
rendering of it, situations become more complicated due to the complexity
of low level graphical primitive and multi-threading. In case you get a red
area, you can render the view in a mock canvas. This can be done using:
v := RTView new.
es := RTBox elementsOn: (1 to: 10).
v addAll: es.
RTGridLayout on: es.
v canvas buildMorph drawOnMockCanvas

You need to execute using the do-it menu option.


Chapter 6

Coloring

Colors plays an important role in a visualization. It may indicate particular


situations without requiring a long visual processing time. This mechanism
is called pre-attentive perception and is key to efficiently and quickly convey-
ing information.
Roassal offers several mechanisms to easily associate colors to elements.
This chapter gives an overview of coloring in Roassal.

6.1 Palettes

Roassal contains a number of color schemas, available in the class RTPalette.


Consider the example (Figure 6.1):

v := RTView new.
es := (RTBox new color: [ :index | RTPalette c3 at: index ]) elementsOn: (1 to: 5).
v addAll: es.
RTHorizontalLineLayout new gapSize: 1; on: es.
v

This simple example creates a view referenced by the variable v. A shape


is created by instantiating the class RTBox and setting a block as a color. This
block is evaluated for each element. Elements are created with elementsOn: (1
to: 5): five elements are created, each having a number as the element model.
The shape evaluates the block provided as a color by using the model as
the index variable. The palette given by RTPalette c3 has 5 different colors,
represented as an Array of the same size.
The class RTPalette has several palettes. You can replace the c3 from any
value ranging from c1 to c14.
82 Coloring

Figure 6.1: Example of a palette.

Figure 6.2: Simple coloring.

6.2 Multi-linear color normalizer


In the Roassal jargon, a normalizer is a facility to map a set of objects over a
range of values or colors. Several color normalizers are provided to color an
element according to some particular requirements.
The RTMultiLinearColor class is a simple and useful color normalizer. It
associates a color to each element depending on a numerical value, ranging
from 0.0 to 1.0. Consider the following example (Figure 6.2):
v := RTView new.
n := RTMultiLinearColor new.
shape := RTBox new size: 30; color: n.
elements := shape elementsOn: (0.0 to: 1.0 by: 0.05).

v addAll: elements.
RTGridLayout on: elements.
v

The code given above first creates a view. It then creates an instance of
RTMultiLinearColor. This normalizer is polymorph to the class Color, which
means you can provide a color normalizer where a color or a block is usu-
ally expected. The example simply uses the normalizer as a color by being
provided to color:.
Multi-linear color normalizer 83

Figure 6.3: Simple coloring.

The default configuration of RTMultiLinearColor is to map a value ranging


from 0.0 to 1.0 to the default color palette, composed of red, blue, and green.
The elements created with shape elementsOn: (0.0 to: 1.0 by: 0.05) have a
numerical value as model. This value is directly mapped to a color.
A particular palette may be set using color:. The following example maps
the value from red to gray (Figure 6.3):
v := RTView new.
n := RTMultiLinearColor new.
n colors: { Color red . Color gray }.
shape := RTBox new size: 30; color: n.
elements := shape elementsOn: (0.0 to: 1.0 by: 0.05).

v addAll: elements.
RTGridLayout on: elements.
v

The example above uses a palette made of two colors, red and gray. These
two colors are then interpolated to give a value ranging from 0.0 and 1.0. The
value 0.0 is mapped to red, and 1.0 to gray.
In the examples given above each element has a numerical value as object
model. It does not have to be so. Consider the following example (Figure
6.4):
classes := Collection withAllSubclasses.
maximumNumberOfLinesOfCode := (classes collect: #numberOfLinesOfCode)
max.

v := RTView new.
n := RTMultiLinearColor new.
n colors: RTPalette c4.
n command: [ :cls | cls numberOfLinesOfCode / maximumNumberOfLinesOfCode
].

shape := RTBox new color: n.


elements := shape elementsOn: classes.
84 Coloring

Figure 6.4: Classes and their size indicated using a color.

v addAll: elements.
RTGridLayout new gapSize: 1; on: elements.

The variable classes contains a collection of classes to be represented. The


largest number of lines of code is obtained from the collection classes. The
message max returns the maximum value of a set of numerical values (e.g.,
#(1 2 42 3 4) max returns 42).
The normalizer n uses a palette as the colors to be used. The message
command: takes a block or a symbol as argument to compute the color index.
The block value has to return a numerical value ranging from 0.0 to 1.0.
The visualization can be enriched with edges and a dedicated legend (Fig-
ure 6.5):
classes := Collection withAllSubclasses.
maximumNumberOfLinesOfCode := (classes collect: #numberOfLinesOfCode)
max.

v := RTView new.
n := RTMultiLinearColor new.
n colors: RTPalette c4.
n command: [ :cls | cls numberOfLinesOfCode / maximumNumberOfLinesOfCode
].

shape := RTEllipse new size: 13; color: n.


elements := shape elementsOn: classes.
Multi-linear color normalizer 85

v addAll: elements.

eb := RTEdgeBuilder new.
eb view: v.
eb moveBehind.
eb connectFrom: #superclass.

RTClusterLayout new on: elements.

"Add the legend"


lb := RTLegendBuilder new.
lb view: v.
lb addText: 'Each circle is a class'.
lb addText: 'Edges indicate inheritance between classes'.
lb addText: '(Subclasses are closer to the border than their superclass)'.
lb addColorFadingUsing: n colors text: 'Number of lines of code'.
lb build.

It is common to have to normalize colors according to a particular metric.


In the previous example, we did it manually as we needed to compute the
maximum number of lines of code. Computing maximum and minimum
values is often cumbersome.
Roassal offers a dedicated infrastructure, called RTMetricNormalizer, to
ease the color normalization using a metric. Use of this normalizer makes
unnecessary to compute the largest size in the set. Consider this new ver-
sion (without legend to keep the code short):
classes := Collection withAllSubclasses.

v := RTView new.

shape := RTEllipse new size: 13.


elements := shape elementsOn: classes.

v addAll: elements.
RTMetricNormalizer new
elements: elements;
normalizeColor: #numberOfLinesOfCode using: RTPalette c4.

eb := RTEdgeBuilder new.
eb view: v.
eb moveBehind.
eb connectFrom: #superclass.

RTClusterLayout new on: elements.


86 Coloring

Figure 6.5: Adding a legend.

6.3 Coloring objects


The class RTMultiLinearColorForIdentity colors a Roassal element based on the
object model. A color is given to each object model. Elements representing
the same object will have the same color. Consider the following example
(Figure 6.6):
v := RTView new.

wordsToHighlight := #('all' 'me' 'you').


normalizer := RTMultiLinearColorForIdentity new.
normalizer objects: wordsToHighlight.
shape := RTBox new size: 40; color: normalizer.
Coloring objects 87

Figure 6.6: Example of RTMultiLinearColorForIdentity.

strings := 'you and you and me make us all' substrings.


es := shape elementsOn: strings.
v addAll: es.
es @ RTLabeled.
es @ RTPopup.
RTHorizontalLineLayout new gapSize: 1; on: es.
v

The example above represents a set of words and highlights some of


them. The variable wordsToHighlight contains the words that have to be high-
lighted. The normalizer is configured with the words using objects:. The
elements to display are created by having to cut the string ’you and you and
me make us all’ into single words using #substrings.
Consider the following example using the famous French poem by Jean
de La Fontaine (Figure 6.7)
v := RTView new.
poem := 'La cigale ayant chante
Tout l''ete,
Se trouva fort depourvue
Quand la bise fut venue :
Pas un seul petit morceau
De mouche ou de vermisseau.
Elle alla crier famine
Chez la fourmi sa voisine,
La priant de lui preter
Quelque grain pour subsister
Jusqu''a la saison nouvelle.
" Je vous paierai, lui dit-elle,
Avant l''aout, foi d''animal,
Interet et principal. "
La fourmi n''est pas preteuse :
C''est la son moindre defaut.
" Que faisiez-vous au temps chaud ?
Dit-elle a cette emprunteuse.
88 Coloring

Figure 6.7: Visualizing a poem.

- Nuit et jour a tout venant


Je chantais, ne vous deplaise.
- Vous chantiez ? J''en suis fort aise :
Eh bien ! Dansez maintenant.'.

wordsToHighlight := #('fourmi' 'vous' 'animal' 'a' 'de' 'la').


normalizer := RTMultiLinearColorForIdentity new.
normalizer objects: wordsToHighlight.
shape := RTBox new color: [ :w | normalizer rtValue: w ].
strings := poem substrings.
es := shape elementsOn: strings.
v addAll: es.
es @ RTPopup.
RTGridLayout new gapSize: 1; on: es.
v
Chapter 7

Interactive Visualization

Roassal elements (and Trachel shapes) may react to user events such as
mouse clicking, mouse moving and key pressing. Making a visualization
interactive is important to enable the following: navigate within a data
set, drill-down/up, details on demand without overloading a visualization.
Roassal offers a number of interaction, implemented as a subclass of RTInter-
action.

7.1 Draggable elements and view


Elements may be draggable using the interaction RTDraggable and a view
may be draggable using RTDraggableView. Interaction may be set on one ele-
ment or a group of elements, using the message @. Consider the following
example:
v := RTView new.
v @ RTDraggableView.
es := RTBox elementsOn: (1 to: 20) asArray shuffled.
es @ RTDraggable.
es @ RTPopup.
v addAll: es.

RTMetricNormalizer new
elements: es;
normalizeColor: #yourself;
normalizeSize: #yourself.

RTFlowLayout on: v elements.


v
90 Interactive Visualization

Figure 7.1: Simple example without interaction.

Interactions may be added at anytime to the element and the view. The
instruction es @ RTDraggable may be located before or after adding the ele-
ments in the view.

7.2 Popup
A popup is a contextual information that appears when the mouse is above
an element. The information is removed when the mouse leaves the ele-
ment. Adding the expression es @ RTPopup makes the element react to
mouse movements.
Without being configured, as it is the case with the expression es @ RT-
Popup, the popup content is directly generated from the model behind the
pointed element. For example:
v := RTView new.
es := RTEllipse elementsOn: (1 to: 100).
es @ RTPopup.
v addAll: es.
RTGridLayout on: es.
v

Each circle represents a number. Pointing to an element using the mouse


open a small number that indicates the pointed number. The popup may be
configured in a number of ways.
For example, the following example shows different combinations of
popup configuration:
Fixed popup 91

v := RTView new.
es := RTEllipse elementsOn: (1 to: 100).
popup := RTPopup new.
popup alphaPopupBackground: 0.5.
popup backgroundColor: Color blue.
popup borderColor: Color red.
popup textColor: Color orange.
popup text: [ :object | 'Value ', object asString ].

es @ popup.
v addAll: es.
RTGridLayout on: es.
v

A popup supports multi-lined text. For example, you may insert a car-
riage return character in the text: instruction:
...
popup text: [ :object | 'Value = ', String cr, object asString ].
...

7.3 Fixed popup


A fixed popup is like a popup, but it is located at the bottom left of the
window. This is useful when the popup embeds a long text, or the popup
negatively interferes with the visualization.
The class RTFixedPopup is a subclass of RTPopup, as a consequence, you
can simply replace the use of RTPopup by RTFixedPopup to have a fixed
popup.

7.4 Graphical popup


Examples given previously in this chapter use text as a popup content. A
popup does not necessarily need to render text and may render any arbitrary
visualization.
Consider the following example (Figure 7.2):
v := RTView new.

labels := RTLabel elementsOn: (1 to: 10).

popup := RTPopup new.


92 Interactive Visualization

Figure 7.2: Graphical popup.

popup group: [ :aGroup :element |


| es |
es := (RTEllipse new size: 10; color: Color red) elementsOn: (1 to: element
model).
aGroup addAll: es.
RTGridLayout on: es.
].
labels @ popup.
v addAll: labels.
RTGridLayout on: labels.
v

The message group: takes as argument a block with two arguments. The
first argument, called aGroup in our example, receives an empty group of
elements. The block is intended to add elements in the aGroup variables.
Those elements will be used by the popup. Layout may be applied.
Edges may be also added. Consider (Figure 7.3):
v := RTView new.

es := RTLabel elementsOn: (5 to: 100 by: 5).

v addAll: es.
Graphical popup 93

Figure 7.3: Popup using edges.

es @ (RTPopup new
background: Color blue
group: [ :group :el |
| elements |
elements := RTEllipse elementsOn: (1 to: el model).
group addAll: elements.
RTEdgeBuilder new
view: group;
elements: elements;
connectFrom: [ :aNumber | aNumber // 2 ].
RTClusterLayout new
verticalGap: 0;
on: elements.
]).

RTHorizontalLineLayout on: es.


v

The edge builder is set with group as the view. This is where the
edge builder will look into elements to link. Methods alternative to back-
ground:group: may be used, such as group: if the background is not necessary.
A title is added to the popup by using named:background:group: as in:
94 Interactive Visualization

...
es @ (RTPopup new
named: [ :aNumber | 'Cluster for ', aNumber asString ]
background: Color blue
group: [ :group :el |
...
]
...

7.5 Highlighting elements


Reacting to the mouse movement by highlighting related elements is a fre-
quent need. The interaction class RTHighlightable is a flexible interaction to
highlight particular elements. Consider the following code snippet (Figure
7.4):
v := RTView new.
es := RTEllipse elementsOn: (20 to: 70 by: 5) asArray shuffle.
v addAll: es.
es @ RTPopup.
RTMetricNormalizer new
elements: es;
normalizeSize: #yourself;
normalizeColor: #yourself.
RTFlowLayout on: es.

es @ RTHighlightable.
v

When the mouse enters an element, the color of that element changes.
When the mouse leaves, the original color is restored. The default highlight-
ing color is blue. It can be set using the color: message. For example, the
following instruction es @ (RTHighlightable new color: Color pink) set the color
to pink.
A set of object models or Roassal elements may be provided using high-
light: or highlightElements:. Consider the second version of the previous script:
v := RTView new.
es := RTEllipse elementsOn: (20 to: 70 by: 5) asArray shuffle.
v addAll: es.
es @ RTPopup.
RTMetricNormalizer new
elements: es;
normalizeSize: #yourself;
normalizeColor: #yourself.
Highlighting elements 95

Figure 7.4: Simple example without interaction.

RTFlowLayout on: es.

es @ (RTHighlightable new highlight: [ :aNumber | aNumber to: 70 by: 5] ).


v

Object models greater than the one on which the mouse is above are high-
lighted.

v := RTView new.
es := RTEllipse elementsOn: (20 to: 70 by: 5) asArray shuffle.
v addAll: es.
es @ RTPopup.
RTMetricNormalizer new
elements: es;
normalizeSize: #yourself min: 5 max: 30;
normalizeColor: #yourself.
RTFlowLayout on: es.

es @ (RTHighlightable new highlightElements: [ :element |


element view elements select: [ :e | e trachelShape height > 15 ] ] ).
v

Both highlight: and highlightElements: accept a one-argument block. The


argument block as to return a collection of object models or a collection of
Roassal elements, respectively.
96 Interactive Visualization

Figure 7.5: Dynamically adding edges.

7.6 Dynamically adding labels and edges


Labels and edges may be added as a kind of popup. Consider the following
example (Figure 7.5):
v := RTView new.
es := (RTBox new color: (Color blue alpha: 0.4))
elementsOn: (RTShape withAllSubclasses).
v addAll: es.

RTNest new
for: es add: [ :group :model |
group addAll: (RTBox new elementsOn: model methods).
RTGridLayout on: group ].

es @ (RTShowEdge new
connectTo: #dependentClasses;
shape: (RTLine new color: Color red);
yourself).
es @ (RTShowLabel new
color: Color red;
highlight: #dependentClasses; top; yourself).
RTGridLayout on: es.
v

The interactions RTShowEdge and RTShowLabel are used. RTShowEdge


Dynamically adding labels and edges 97

defines some edges connecting to dependentClasses using the shape provided


to shape:. RTShowLabel temporarily gives a label to related elements. In the
example given above, the labels are located on the top of an element.
Chapter 8

Applying Layout

Note:
This chapter was written with the participation of
Peter Uhnák ([email protected]).

A layout is the visualization representation along a two-dimensional plan


of elements, typically edges and nodes. A layout makes a visualization not
only aesthetic but also comprehensible. Roassal offers numerous different
layouts and supports the composition of layouts.
Consider the following example (Figure 8.1):
view := RTView new.
es := (RTEllipse new size: 12; color: Color blue)
elementsOn: (1 to: 30).
view addAll: es.

RTEdgeBuilder new
view: view;
elements: es;
connectFrom: [ :value | value // 2 ].

es do: [ :each | each translateTo: (250 atRandom) @ (250 atRandom) ].


view

The script builds some elements and edges that link these elements. Each
element is translated to a random location. Applying the RTTreeLayout layout
highlights the structure from the connection between the elements (Figure
8.2):
view := RTView new.
es := (RTEllipse new size: 12; color: Color blue)
100 Applying Layout

Figure 8.1: Graph with randomly placed nodes.

Figure 8.2: Tree layout.

elementsOn: (1 to: 30).


view addAll: es.

RTEdgeBuilder new
view: view;
elements: es;
connectFrom: [ :value | value // 2 ].

es do: [ :each | each translateTo: (250 atRandom) @ (250 atRandom) ].


RTTreeLayout on: es.
view

The layout reveals that the elements form a binary tree and one can dis-
tinguish what the top root node is and what the leaves are.
Roassal offers over 30 different layouts, each useful in its own rights.
Element-based Layouts 101

8.1 Element-based Layouts


Element-based layouts are a particular set of layouts in Roassal which do
not consider edges to determine element locations. Instead of using edges,
an element-based layout uses element size, shape or position within a group.
Edges are not forbidden: they are simply not used by the layout.

Circle Layouts
Circle layouts arrange elements along a geometrical circle. The order of the
elements, as with all circle-based layouts, is the same as the collection on
which the layout operates.
v := RTView new.

es := (RTEllipse new size: 12) elementsOn: (1 to: 10).


v addAll: es.
es @ RTLabeled.

RTMetricNormalizer new
elements: es;
normalizeColor: #yourself using: (Array with: Color red with: Color lightRed).

RTEdgeBuilder new
view: v;
elements: es;
connectFrom: [ :value | value // 2 ].

RTCircleLayout new on: es.


v

A circular layout may be parametrized along several properties:


RTCircleLayout new
initialIncrementalAngle: 30 degreesToRadians;
initialAngle: 15 degreesToRadians;
initialRadius: 200;
on: es.

Without any option, the circle layout distributes all the elements evenly
along a circle (2pi/elements size). Additionally radius can be either set abso-
lutely via initialRadius:, or as a scalable factor scaleBy: - then the radius will
be elements size * scaleFactor.
It is important to note that RTCircleLayout does not take into considera-
tion the size of the elements; this is enough when the elements are uniform,
however if their sizes vary, different layouts may be considered.
102 Applying Layout

Figure 8.3: Circle layout applied on some elements .

Figure 8.4: Some options of Circular Layout.

Variants of the default circle layout, named equidistant and weighted lay-
outs, are also available:

• RTEquidistantCircleLayout makes sure that there is the same distance be-


tween each element.

• RTWeightedCircleLayout on the other hand adds spacing based on the


Element-based Layouts 103

Figure 8.5: Equidistant (left) and Weighted (right) layout with non-uniform
sizes.

size of the elements. Thus there will be less space between smaller
elements, and more space between large ones.

So now if we apply layout on non-uniform elements we get:

v := RTView new.
elements := (RTEllipse new color: Color red; size: [:vv | vv * 4 ])
elementsOn: (1 to: 10).
v addAll: elements.
RTEquidistantCircleLayout on: elements.
elements translateBy: -150 @ 0.
v add: ((RTLabel new elementOn: 'Equidistant Layout') translateTo: -40 @ 100).

es := (RTEllipse new color: Color blue; size: [:vv | vv * 4 ])


elementsOn: (1 to: 10).
v addAll: es.
RTWeightedCircleLayout on: es.
es translateBy: 150 @ 0.
v add: ((RTLabel new elementOn: 'Weighted Layout') translateTo: 260 @ 100).
v

Flow Layouts

A flow layout arranges elements in a ’flowing’ manner. While we could


consider circle layouts to be also a flow in a clockwise direction, layouts
presented here provide flow by lines and columns.
104 Applying Layout

Flow and Grid Layouts

The flow layout arranges elements in lines, each line flowing from left to
right; Horizontal flow on the other hand is in columns, flowing from top to
bottom.

v := RTView new.

shape := RTCompositeShape new.


shape add: RTLabel new.
shape add: (RTBox new
color: Color transparent; borderColor: Color black).
es := RTGroup
with: (shape elementOn: RTFlowLayout)
with: (shape elementOn: RTHorizontalFlowLayout)
with: (shape elementOn: RTGridLayout)
with: (shape elementOn: RTCellLayout).
v addAll: es.

RTNest new
for: es
inShape: #second
add: [ :group :layout |
group addAll: ((RTBox new size: [:m | m * 10])
elementsOn: (1 to: 6)).
layout new on: group.
].

RTCellLayout new gapSize: 10; on: es.

(v elements allButFirst: 4) @ (RTLabeled new


color: Color black; center).
v

Flow layouts vertically line up elements according to their size. The


layout may be configured with a maximum total width (maxWidth:). For
Grid and Cell layout this limit is instead number of items in the line
(lineItemsCount:). By default a flow layout try to fill a roughly rectangular
area, while a grid layout approximate the golden ratio.

Alignment

Cells in Flow layouts can be aligned:


To align RTHorizontalFlowLayout use alignTop for left, and alignBottom for
right alignment.
Element-based Layouts 105

Figure 8.6: Flow and Grid Layouts.

Figure 8.7: RTFlowLayout alignments.

Figure 8.8: RTVerticalLineLayout and RTHorizontalLineLayout.

Line Layouts

Two line layouts are offered: RTVerticalLineLayout and RTHorizontalLineLayout


to layout, vertically and horizontally, respectively (Figure 8.8).
Here is an example of the horizontal line layout:
v := RTView new.
106 Applying Layout

Figure 8.9: RTTreeLayout demonstrating gap sizes.

es := (RTBox new size: #yourself) elementsOn: (10 to: 40 by: 5).


v addAll: es.
RTHorizontalLineLayout on: es.
v

Replacing RTHorizontalLineLayout by RTVerticalLineLayout triggers the verti-


cal line layout instead.

8.2 Edge-driven layouts


Edge-driven layouts determine the location of an element based on the edges
linking these elements.

Tree Layout
The beginning of this chapter gives an example of using the tree layout.
Note that in the picture above the horizontalGap is applied only to the
leaves of the tree; distance between parents is then accommodated automat-
ically, so no overlapping or crossing occurs. The tree Layout orients the tree
vertically, while the RTHorizontalTreeLayout uses an original orientation, from
left to right.

Radial Tree Layout


One problem with trees is that they tend to have many leaves which often
results in very wide visualizations. One way to deal with this problem is to
present the tree circularly. Since each new layer increases the radius of the
circle, the overall element structure accommodates the space better.
v := RTView new.
Edge-driven layouts 107

Figure 8.10: Comparison of Horizontal, Vertical and Radial Tree Layouts.

elsBuilder := [ | es |
es := (RTEllipse new size: 12) elementsOn: (1 to: 40).
v addAll: es.

RTEdgeBuilder new
view: v;
elements: es;
connectFrom: [ :value | value // 2 ].

RTMetricNormalizer new
elements: es;
normalizeColor: #yourself using: (Array with: Color red with: Color green).
es
].

g := RTGroup with: elsBuilder value with: elsBuilder value with: elsBuilder value.

RTTreeLayout new on: g first.


RTHorizontalTreeLayout new on: g second.
RTRadialTreeLayout new on: g third.

RTRectanglePackLayout new gap: 0.1; on: g.


v

Note that RTRadialTreeLayout may produce an odd result in the presence


of edge cycles.
108 Applying Layout

Figure 8.11: RTDominanceTreeLayout showing dependencies between


RTShape classes.

Dominance Tree Layout


This layout is especially useful for visualizing dependencies and flow charts,
since it organizes elements in such a manner that the flow of the graph is
emphasized.
v := RTView new.
v @ RTDraggableView @ RTZoomableView.
classes := RTShape withAllSubclasses asGroup.
es := (RTEllipse new size: 15; color: Color blue) elementsOn: classes.
v addAll: es.

eb := RTEdgeBuilder new.
eb view: v; objects: classes.
eb shape arrowedLine; color: Color red.
eb
connectFrom: #yourself toAll: #dependentClasses.

RTDominanceTreeLayout new
verticalGap: 30;
horizontalGap: 15;
on: es.
v

Cluster Layout
Cluster is visually similar to radial tree; it groups related elements together
(Figure 8.12:.
view := RTView new.
view @ RTDraggableView @ RTZoomableView.

es := (RTEllipse new size: 12) elementsOn: (4 to: 100).


view addAll: es.
Edge-driven layouts 109

Figure 8.12: Four trees clustered together.

edges := RTGroup new.


es copy do: [ :e |
| fromE |
fromE := es elementFromModel: (e model // 2).
fromE ifNotNil: [ edges add: (RTLine edgeFrom: fromE to: e) ].
].
view addAll: edges.

colorize := nil.
colorize := [ :root :color |
root outgoingEdges do: [ :edge |
edge shape color: color.
edge signalUpdate.
colorize value: edge to value: color.
].
].

colorize value: es first value: Color red.


colorize value: es second value: Color green.
colorize value: es third value: Color blue.
colorize value: es fourth value: Color orange.

RTClusterLayout new on: es.


view

The difference between the cluster and radial layouts has to do with the
layers forming circles.
110 Applying Layout

Figure 8.13: Sugiyama layout applied on hierarchy of ‘RTLayout‘ classes.

Sugiyama
Sugiyama layout is a hierarchical layered layout. It places elements in hi-
erarchical order such that edges goes from higher layer to lower layer. At
the same time the layout minimizes the amount of layers and edge crossings
(Figure 8.13).
classes := RTLayout withAllSubclasses.
b := RTMondrian new.
b shape ellipse color: Color purple; size: 5.
b nodes: classes.
b edges connectFrom: #superclass.
b layout sugiyama.
b

Force Based Layout


Force Based Layout applies force between related elements similar to elec-
trical charge. Thus related elements will repulse each other. The charge is
usually negative since it represents repulsion. Additionally to charge: you
can also specify strength:, which is the strength of the bonds (edges) between
elements.
v := RTView new.

es := RTEllipse elementsOn: Collection withAllSubclasses.


v addAll: es.
Edge-driven layouts 111

Figure 8.14: RTForceBasedLayout used to layout hierarchy of classes.

RTEdgeBuilder new
elements: es;
view: v;
moveBehind;
connectToAll: #dependentClasses.

RTForceBasedLayout on: es.

RTMetricNormalizer new
elements: es;
normalizeColor: #numberOfMethods;
normalizeSize: #numberOfMethods.
v

The force based layout is appealing due to its simplicity of use and nice
results that it produces. However, it badly scales. Performing a force based
layout on hundreds of nodes and edges may take hours (literally). Before
performing this layout on a large graph, one may want to try it on a reduced
graph.
112 Applying Layout

Figure 8.15: Pack of different elements.

Rectangle Pack

RTRectanglePackLayout packs all the elements as tightly as possible. It uses an


element’s bounding box, so using circles or polygons instead of boxes will
have no effect. One use for this layout is to provide comparative views of
some elements — name clouds, source code size of classes, etc.

v := RTView new.
v addAll: ((RTEllipse new color: (Color red alpha: 0.3)) elementsOn: Collection
withAllSubclasses) @ RTPopup.
RTMetricNormalizer new
elements: v elements;
normalizeSize: #numberOfLinesOfCode min: 10 max: 60;
normalizeColor: #numberOfMethods using: (Array with: Color gray with: Color
red ).
RTRectanglePackLayout on: v elements.
v

RTNameCloud also internally uses RTRectanglePackLayout

b := RTNameCloud new
addString: 'open
| v shape |
v := RTView new.
shape := RTLabel new height: [ :assoc | assoc value ]; text: #key.
v addAll: (shape elementsOn: table associations).
RTFlowLayout on: v elements.
v open'.
b
Layout builder 113

Figure 8.16: RTRectanglePackLayout used to layout a name cloud.

8.3 Layout builder


The class RTLayoutBuilder offers expressive ways to build and compose lay-
outs. The layout builder is offered by Mondrian. Consider the following
Mondrian script (Figure 8.17):
b := RTMondrian new.
b nodes: RTShape withAllSubclasses.
b shape line color: Color blue trans.
b edges
connectToAll: [ :c | c dependentClasses copyWithout: c superclass ].
b layout
force.
b

The script represents each subclass of RTShape as a box. Edges represents


dependencies between classes, without considering inheritance. We made
this example to produce lonely classes. It often happens that lonely classes
need a particular layout.
The message ifNotConnectedThen: takes as argument a layout (i.e., an in-
stance of a subclass of RTLayout). The provided layout is applied only to
elements that are not connected (i.e., with no incoming or outgoing edges).
b := RTMondrian new.
b nodes: RTShape withAllSubclasses.
b shape line color: Color blue trans.
b edges
connectToAll: [ :c | c dependentClasses copyWithout: c superclass ].
114 Applying Layout

Figure 8.17: Dependencies between classes.

Figure 8.18: Conditional layout.

b layout
force;
ifNotConnectedThen: RTGridLayout new.
b

Using the composition of layout and the layout builder (Figure 8.19):
classes := RTLayout withAllSubclasses, RTBuilder withAllSubclasses, RTShape
withAllSubclasses.
Layout builder 115

Figure 8.19: RTForceBasedLayout used to layout hierarchy of classes.

b := RTMondrian new.
b shape circle size: 5.
b nodes: classes.
b edges connectFrom: #superclass.

b normalizer
objects: classes;
normalizeSize: #numberOfMethods min: 5 max: 30;
normalizeColor: #numberOfLinesOfCode using: (Array with: Color green with:
Color red ) using: #sqrt.

b layout
for: [ :c | c includesBehavior: RTLayout ] use: RTForceBasedLayout new;
for: [ :c | c includesBehavior: RTBuilder ] use: RTForceBasedLayout new;
for: [ :c | c includesBehavior: RTShape ] use: RTForceBasedLayout new;
flow.
b

Several layouts are available:

• force for a force based layout (also called spring layout)


• horizontalLine for the horizontal line layout
• verticalLine to vertically line up elements
• tree to have a vertical tree
• horizontalTree for an horizontal tree, roots are on the left-hand side and
leaves on the right-hand side
• cluster and radial for radial-based layout
116 Applying Layout

• sugiyama for the Sugiyama layout, which is a vertical tree layout that
minimizes edges crossing

8.4 Creating custom layout


If you want to add your own layout you just need to subclass RTLayout and
implement RTLayout>>doExecute: elements.
For more fine-graded control you have three main methods available.
RTLayout>>executeOnElements: elements
self doInitialize: elements.
self doExecute: elements asOrderedCollection.
self doPost: elements.

1. doInitialize: can be used for element preprocessing, if needed. For exam-


ple RTAbstractGraphLayout uses this for removing cycles from the graph,
so the layouts can work only with trees.
2. doExecute: is the main method, and the only method that must be im-
plemented. Perform your layout here.
3. doPost: allows one to insert post-processing actions.
Chapter 9

Using the Camera

A Roassal view and therefore a Trachel canvas is seen through a camera.


The direction of the camera is always perpendicular to the view, else Roassal
would be a 3D engine and not a 2D engine. Camera may (i) go up and down,
which is perceived by zooming out and in and (ii) be horizontally translated,
which is perceived as scrolling the whole scene.

9.1 Operations
A camera is accessible from the canvas and is described by the class TRCam-
era. This class defines the following methods:

• translateTo: to translate the camera to a given point. This method takes


a point as argument.
• translateBy: for a step-wise translation. This method takes also a point
as argument.
• scale: for zooming in and out. This method takes a float number as
argument. An argument greater than 1.0 is perceived as a zoom in,
and less than 1.0 as a zoom in.

9.2 Initialization
When a view is opened, the camera is positioned at the center of it. This is
the defaut behavior of a camera. This behavior may be changed in case you
need to position the camera at a different location. Consider the following
example:
118 Using the Camera

v := RTView new.
map := RTOSM new.
e := map element.

v add: e.

paris := 48.8567 @ 2.3508.

v @ RTDraggableView.

v canvas camera translateTo: (map latLonToRoassal: paris).


v canvas camera noInitializationWhenOpen.
v

This short script use the OpenStreeMap integration to move the camera
above Paris. Coordinate of Paris are here specified in latitude and longitude.
The call to latLonToRoassal: converts the point argument, specified as latitude
@ longitude, into the Roassal space. The camera is then moved to the com-
puted point.
Part II

Builders
Chapter 10

Domain-Specific
Visualization Made Easy
with Builders

The Roassal visualization engine is a generic purpose engine for visualizing


data. Roassal has been designed to easily map any arbitrary Pharo object
structure, metrics, properties to visual dimensions.
Frequently, visualizations have the same limitation than software artifact:
if not properly designed, a visualization is difficult to extend and reuse in a
context that is different from the one for which it had been originally de-
signed. In software engineering, problems traditionally associated with soft-
ware reuse, composition, and extension are tremendously alleviated by em-
ploying domain-specific languages: a reduced and specialized language for
a particular application domain lowers the effort of designing and maintain-
ing software.
Roassal supports a generic infrastructure, called builder, to efficiently
build and reuse visualizations. Builder follows the key principles of domain-
specific languages by supporting the definition of domain-specific visualiza-
tions, i.e., visualizations tailored to a particular domain. Builder offers a set
of reusable building blocks to easily define visual shapes, interaction and
layout over any arbitrary domain. A builder encapsulates the logic of a visu-
alization and maps a particular domain to visual Roassal elements.
The builder infrastructure is composed of five key classes, which are re-
viewed along this chapter.
This chapter was written with the participation of Yuriy Tymchuk
([email protected])
122 Domain-Specific Visualization Made Easy with Builders

Figure 10.1: Second version of the punchcard builder.

10.1 Punch card example


This section describes the builder infrastructure in a tutorial-like fashion.
The example we use throughout this paper is a punch card, as shown in
Figure 10.1.
Metrics are vertically located on the left hand side and elements are hor-
izontally located. The figure shows that the element RTBuilder and RTPunch-
card have high values for the metrics NOM, NOA, and LOC.

10.2 Defining a builder with RTBuilder


The class RTBuilder translates a user-defined domain into low level instruc-
tions using the Roassal API. A builder essentially wraps a Roassal view and
offers dedicated (sub-)builders, for edges, nodes, interaction, described in
the following sections.
A punchcard is defined mapping objects and metrics to some visual di-
mensions (e.g., elements size and colors). Computing the metrics over the
objects results in a grid. The input of the builder will therefore be objects
and metrics.
So far, most of the visualizations have been defined by typing in a play-
ground. The playground is made to easily build script and try out relatively
short pieces of code. As soon as "serious" code has to be written, such as a
builder, you need to move away from the playground and use the Nautilus
System Browser. The System Browser is open from the World menu.
The very first step, as for any code writing in Pharo, you need to define a
package. The package Punchcard is created by right-clicking on the package
pane (top-left textual pane) in a system browser.
A builder is created by subclass the class RTBuilder. We therefore create a
subclass of it, called RTPunchcard :
RTBuilder subclass: #RTPunchcard
instanceVariableNames: 'metrics objects'
classVariableNames: ''
package: 'Punchcard'
Defining a builder with RTBuilder 123

As seen in the chapter about the Pharo language, the class RTPunchcard
contains two instance variables, called metrics and objects. These instance
variables have to be initialized. Defining an initialize method in RTPunchChard
is therefore our next step:
initialize
super initialize.
objects := OrderedCollection new.
metrics := OrderedCollection new.

All the methods provided in this chapter are defined in the class we have
just defined. An end-user defines a visualization by specifying objects and
metrics. Two methods have to be provided for that purpose:
addMetric: blockOrSymbol named: aName
metrics add: aName -> blockOrSymbol

addObject: anObject
objects add: anObject

The method renderIn: is key in the builder framework. This is where the
visualization is constructed, i.e., Roassal elements are created and the view
is filled. The method renderIn: is intended to be overridden when creating
a new builder class. Here is a first version of the method renderIn: which
displays a list of objects, and computes the metrics for each object:
renderIn: aView
aView add: (RTLabel elementsOn: ' ').
objects
do: [ :object | aView add: (RTLabel elementOn: object) ].

metrics do: [ :assoc |


aView add: (RTLabel elementOn: assoc key).
objects do: [ :object |
| value |
value := assoc value rtValue: object.
aView add: ((RTEllipse new size: value) element) ] ].

RTCellLayout new
lineItemsCount: objects size + 1;
on: aView elements

Line 1 inserts an empty label, used for the first column. Line 3 - 4 inserts a
label for each object. Line 6 - 11 inserts the name of each metric and compute
the metrics against the objects. Lines 13 - 15 layout the elements as a grid.
At that stage, the builder may be invoked as:
124 Domain-Specific Visualization Made Easy with Builders

Figure 10.2: First version of the punchcard builder.

b := RTPunchcard new.
b addObject: RTPunchcard.
b addObject: RTBox.
b addObject: RTBuilder.
b addMetric: #numberOfMethods named: 'NOM'.
b addMetric: #numberOfVariables named: 'NOA'.
b

The objects used in the visualization are three classes: RTPunchcard, RT-
Box, RTBuilder. For each of these classes, the messages numberOfMethods and
numberOfVariables will be sent to return a numerical value.
The produced rendering is given in Figure 10.2. At that stage, the visu-
alization is very simplistic: circles may be excessively large if metric values
are high and the exact value behind each circle is not accessible. We address
this in the subsequent sections.

10.3 Specifying shapes with RTShapeBuilder


A shape builder is a particular object to configure shapes and is a factory of
Roassal elements. The code renderIn: method uses the expression RTEllipse
new size: value to define a circle with a size given by each metric value. Size
of an ellipse is therefore linear to the value, which is not really optimal in
case of large disparities between values.
By using a shape builder, the shape may be defined externally from the
renderIn: method.
A builder offers the method createShapeBuilder to initialize the shape
builder used when creating elements, using elementOn:. In our case, we
are interested in having circle shapes of a size given by the element’s object
model. Overriding the method createShapeBuilder allows one to initialize the
shape builder associated to a builder.
createShapeBuilder
| sb |
sb := super createShapeBuilder.
Specifying shapes with RTShapeBuilder 125

Figure 10.3: Second version of the punchcard builder.

sb ellipse size: #yourself.


^ sb

The method renderIn: directly uses RTEllipse, at Line 11. Having this use
of the shape prevents one from changing the shape. Instead, this method can
be rewritten:
renderIn: aView
aView add: (RTLabel elementsOn: ' ').
objects
do: [ :object | aView add: (RTLabel elementOn: object) ].

metrics do: [ :assoc |


aView add: (RTLabel elementOn: assoc key).
objects do: [ :object |
| value |
value := assoc value rtValue: object.
aView add: (self elementOn: value) ] "New line"].

RTCellLayout new
lineItemsCount: objects size + 1;
on: aView elements

The behavior as shown in the previous example is preserved. However,


by using elementOn: to create elements, visual shapes may be particularized
by the end-user. Consider the following example:
b := RTPunchcard new.
b shape rectangle
size: [ :v | v sqrt * 3 ].
b addObject: RTPunchcard.
b addObject: RTBox.
b addObject: RTBuilder.
b addMetric: #numberOfMethods named: 'NOM'.
b addMetric: #numberOfVariables named: 'NOA'.
b addMetric: #numberOfLinesOfCode named: 'LOC'.
b
126 Domain-Specific Visualization Made Easy with Builders

Circles have been replaced with boxes. Each box reflects the square root
of a metric value and the minimum size is set to 5 pixels. The visualization is
still limited. One cannot compare metrics across the different classes. Values
have to be normalized for this.

10.4 Data Normalization


The shape and colors of elements may be resized to reflect a particular prop-
erty when compared with other elements. We call this normalization. Con-
sider the following improvement of renderIn:.
renderIn: aView
| el |
aView add: (RTLabel elementsOn: ' ').
objects do: [ :object |
| objElement |
objElement := RTLabel elementOn: object.
aView add: objElement ].

metrics do: [ :assoc |


aView add: (RTLabel elementOn: assoc key).
self resetCreatedElements.
objects do: [ :object |
| value |
value := assoc value rtValue: object.
el := self elementOn: value.
aView add: el ].
self normalizer elements: self createdElements. "New line"
self normalizer build "New line" ].
RTCellLayout new
lineItemsCount: objects size + 1;
on: aView elements

The builder keeps track of the objects that are created thanks to the call
self elementOn: value. Having the list of created objects is useful to configure
the normalizer. In this case, we consider a line as the scope of the normaliza-
tion.
A more elaborated example is:
b := RTPunchcard new.
b normalizer
normalizeSize;
normalizeColorUsing: { Color green . Color red }.

b addObject: RTPunchcard.
b addObject: RTPunchcard.
Specifying interaction using RTInteractionBuilder 127

Figure 10.4: Third version of the punchcard builder.

b addObject: RTBox.
b addObject: RTBuilder.
b addMetric: #numberOfMethods named: 'NOM'.
b addMetric: #numberOfVariables named: 'NOA'.
b addMetric: #numberOfLinesOfCode named: 'LOC'.
b

Result of the example is given in Figure 10.4. Size and color reflect the
metric value, which enable an easy comparison.

10.5 Specifying interaction using RTInteraction-


Builder
Often a visualization has to offer some interaction facilities to let the user
get details or allow for a navigation. The builder class offers the method
setUpInteractionFor: that lets the user specify interaction with the elements:

renderIn: aView
| el |
aView add: (RTLabel elementsOn: ' ').
objects do: [ :object |
| objElement |
objElement := RTLabel elementOn: object.
self setUpInteractionFor: objElement. "New line"
aView add: objElement ].

metrics do: [ :assoc |


aView add: (RTLabel elementOn: assoc key).
self resetCreatedElements.
objects do: [ :object |
| value |
value := assoc value rtValue: object.
el := self elementOn: value.
aView add: el ].
self normalizer elements: self createdElements.
self normalizer build ].
RTCellLayout new
128 Domain-Specific Visualization Made Easy with Builders

lineItemsCount: objects size + 1;


on: aView elements

Similarly to shapes and normalizer, a list of interactions may be set when


defining a visualization, shown as follows:
b := RTPunchcard new.
b interaction
popupText;
highlightColored: Color blue trans;
action: #browse.

b normalizer
normalizeSize;
normalizeColorUsing: { Color green . Color red }.

b addObject: RTPunchcard.
b addObject: RTBox.
b addObject: RTBuilder.
b addMetric: #numberOfMethods named: 'NOM'.
b addMetric: #numberOfVariables named: 'NOA'.
b addMetric: #numberOfLinesOfCode named: 'LOC'.
b

Right-clicking on the class name will open the menu to open a system
browser on that class (i.e., the result of sending browse to a class).
Chapter 11

Visualizing Polymetric
Graphs using Mondrian

Mondrian is a rich API which offers facilities to describe and render graphs.
Mondrian is an essential piece of Roassal due to its expressiveness and sim-
plicity of use. For any data structure, Mondrian allows mapping of metric
values and properties into visual dimensions, such as shape and colors.
Mondrian is often considered as being the most expressive part of Roas-
sal: highly interactive and flexible visualizations may be the result of just a
few lines of code. Mondrian is often the starting point of the Roassal plat-
form.
An online video illustrates this chapter: http://bit.ly/
Mondrian-AgileVisualization

11.1 A first visualization


As a contrived example, consider the following script (Figure 11.1):
b := RTMondrian new.
b nodes: #('hello' 'world' 'bonjour' 'tout le monde' 'Guten' 'Morgen').
b

Six gray squares are horizontally lined up. Each square corresponds to a
word contained in the array passed to nodes:. Several layouts are available,
thanks to the layout builder (see the description of the layout builder in the
Layout chapter). For example, the force layout is activated as:
b := RTMondrian new.
b nodes: #('hello' 'world' 'bonjour' 'tout le monde' 'Guten' 'Morgen').
130 Visualizing Polymetric Graphs using Mondrian

Figure 11.1: A first example.

b layout force.
b

Edges may be simply added using the edge builder, accessible by sending
edges to the Mondrian builder:

b := RTMondrian new.
b nodes: #('hello' 'world' 'bonjour' 'tout le monde' 'Guten' 'Morgen').
b shape line.
b edges useAssociations: {
'hello' -> 'world' .
'bonjour' -> 'tout le monde' .
'Guten' -> 'Morgen' }.
b layout force.
b

A label may be used instead of a grayed box and the layout may be con-
figured as follows (Figure 11.2):
b := RTMondrian new.
b shape label.
b nodes: #('hello' 'world' 'bonjour' 'tout le monde' 'Guten' 'Morgen').
b shape line.
b edges useAssociations: {
'hello' -> 'world' .
'bonjour' -> 'tout le monde' .
'Guten' -> 'Morgen' }.
b layout force charge: -200.
b

As a slightly more elaborated example, consider the following example:


b := RTMondrian new.

b nodes: (1 to: 300).


b edges connectFrom: [ :value | value // 2 ].

b shape
bezierLineFollowing: [ :value | value // 2 ];
color: Color blue trans.
b edges
A first visualization 131

Figure 11.2: The first example revisited.

notUseInLayout;
connectTo: [ :value | (value / 10) asInteger + (value \\ 10) ].

b layout cluster.
b

The example creates an instance of the class RTMondrian and assigns this
object to the b variable. Nodes are then set in the builder, which are 300
successive numbers, starting from 1. Each node is represented as a little gray
square. Two sets of edges are defined. The first set, described using b edges
connectFrom: [ :value | value // 2 ], models the relation between a value and
value // 2. The message // returns "the integer quotient defined by division
with truncation toward negative" as indicated by the comment of the method
// defined in the class Number (e.g., 9//4 = 2).
The second set of edges, described using b edges notUseInLayout; con-
nectTo: [ ... ], is defined as the sum of the two digits. For a given numerical
value, the expression (value / 10) asInteger returns the first digit or it and value
\ 10 the last digit. Each number is linked to another number representing
the sum of its digits (e.g., 15 is linked to 6). These edges are not taken into
account for the layout as specified with the message notUseInLayout.
The first set of edges uses the default shape for lines, which is straight,
gray, and non-directed. For the second set, a shape is defined as a Bezier line
following the control points given by the expression value // 2, painted in a
translucent blue.
The layout used is cluster, forming a radial-like appearance. For this set
of elements and edges, horizontalTree, tree, radial are all relevant layouts.
132 Visualizing Polymetric Graphs using Mondrian

Figure 11.3: Visualizing numbers and their connections.

11.2 Visualizing CSV data


The previous examples visualized numerical values and define some con-
nections based on a mathematical relation. Any arbitrary objects may be
visualized with Mondrian. Consider the following example that displays
data obtained from a CSV (comma separated file):
"You need to load the plugin Neo JSON and CSV parser, available from the World
menu"
csvContent :=
'Santiago,5.1,Chile
Valparaiso,0.5,Chile
Paris,2.2,France
Nice,0.7,France,
Chile,16,
France,66,'.
values := (NeoCSVReader on: csvContent readStream) upToEnd.

b := RTMondrian new.
Mondrian Script Structure 133

Figure 11.4: Cities and countries.

b shape circle
size: [ :aName |
(Float readFrom: (values detect: [ :a | a first = aName ]) second) log * 30 ];
withTextAbove.

b nodes: (values collect: #first).


b edges connectFrom: [ :aName | (values detect: [ :a | a first = aName ]) third ] .
b layout tree.
b

Figure 11.4 illustrates the result. Data obtained from a CSV description
is used in the visualization. Each city and country is represented as a circle.
The size of the circle represents the number of people living in the city or the
country. We use a logarithmic scale to cope with large variations.
To keep the example concise and self-contained, the CSV content is pro-
vided as a Pharo String. If you wish to have the file externally provided, then
you may simply have
csvContent := 'file.csv' asFileReference contents.
values := (NeoCSVReader on: csvContent readStream) upToEnd.
...

In that case, a file named file.csv has to be in the same folder as your Pharo
image. A full path may be provided.

11.3 Mondrian Script Structure


As illustrated in the example given above, a script in Mondrian essentially
contains five different sections: shape description, node declaration, edge
declarations, layout description, normalization.
The message shape, layout, edges sent to an RTMondrian object returns a
shape, layout builder, and edge builders, respectively. Sending the message
nodes: creates some elements using the shape previously defined.
134 Visualizing Polymetric Graphs using Mondrian

Figure 11.5: Visualizing a class hierarchy.

11.4 Visualizing Software


As a first example, consider the following script (Figure 11.5):
b := RTMondrian new.
b shape rectangle
withBorder;
width: [ :cls | cls numberOfVariables * 5 ];
height: [ :cls | cls numberOfMethods ].

b nodes: Collection withAllSubclasses.


b edges connectToAll: [ :cls | cls subclasses ].
b layout tree.
b normalizer
normalizeColorAsGray: [ :cls | cls numberOfLinesOfCode ].
b

The script visualizes the collection class hierarchy obtained with the ex-
pression Collection withAllSubclasses. Relation between nodes is obtained by
linking each nodes to its subclasses. A tree layout is then set.
Each box of the visualization represents a class. The height of a class indi-
cates its amount of methods that it defines. The width indicates the number
Nesting 135

of variables the class defines. The color indicates the number of lines of code
the class defines (white = the class with the smallest number of lines of code,
black = the heaviest class). Each of Mondrian node is draggable and has a
tooltip.
The normalizer may also give a particular color to an element based on
an attribute or a property. Consider the following example (Figure 11.6):
b := RTMondrian new.
b shape circle.
b nodes: Collection withAllSubclasses.
b shape line color: Color veryLightGray.
b edges
moveBehind;
connectFrom: #superclass.
b layout cluster.
b normalizer
normalizeSize: #numberOfMethods min: 6 max: 40;
distinctColorUsing: #package.
b

The script simply associates a circle to each class, subclass of the class Col-
lection. Edges indicates superclass links. The cluster layout makes subclasses
located around a superclass. Each class has a color, representing the package
of a class. Each package has a color, given by the message distinctColorUsing:.

11.5 Nesting
Mondrian offers a convenient way to nest elements. Each node may act as a
space to which nodes may be added. The message nodes:forEach: has to be
used for that purpose. Consider the following example (Figure 11.7):
b := RTMondrian new.
b nodes: (0 to: 90 by: 10) forEach: [ :each |
b nodes: (1 to: each).
b layout grid
].
b layout flow.
b

There are 10 top level nodes, representing the values 0, 10, 20, ..., up to
90. Each node contains an amount of inner values that correspond to the
number it represents.
Encapsulating nodes may also have a title and be connected (Figure 11.8):
b := RTMondrian new.
b shape rectangle withTextAbove.
136 Visualizing Polymetric Graphs using Mondrian

Figure 11.6: Each circle is a class and its color indicates the class packages.

Figure 11.7: Nesting elements.


Nesting 137

Figure 11.8: Nesting elements.

b nodes: RTShape withAllSubclasses forEach: [ :class |


b nodes: class methods.
b layout grid.
].
b edges connectToAll: #subclasses.
b layout tree.
b

The use of withTextAbove makes each encapsulating node have a title


above. Node color may be set to reflect particular conditions, as in the fol-
lowing example (Figure 11.9):
b := RTMondrian new.
b shape rectangle withTextAbove;
fillColor: Color white; borderColor: Color black.
b nodes: RTShape withAllSubclasses forEach: [ :class |
b shape rectangle
if: [ :m | m numberOfLinesOfCode < 5 ] fillColor: Color green;
if: [ :m | m numberOfLinesOfCode >= 5 ] fillColor: Color orange;
if: [ :m | m numberOfLinesOfCode >= 10 ] fillColor: Color red.
b nodes: (class methods sortedAs: #numberOfLinesOfCode).
b layout grid.
].
b edges connectToAll: #subclasses.
b layout tree.
b

Inner nodes may have a proper shape and be connected. Consider the
following version of the visualization of the Roassal shapes (Figure 11.10):
b := RTMondrian new.
b shape rectangle withTextAbove;
fillColor: Color white; borderColor: Color black.
b nodes: RTShape withAllSubclasses forEach: [ :class |
138 Visualizing Polymetric Graphs using Mondrian

Figure 11.9: Nesting colored elements.

b shape rectangle
if: [ :m | m numberOfLinesOfCode < 5 ] fillColor: Color green;
if: [ :m | m numberOfLinesOfCode >= 5 ] fillColor: Color orange;
if: [ :m | m numberOfLinesOfCode >= 10 ] fillColor: Color red;
size: [ :m | m numberOfLinesOfCode sqrt * 5 ].
b nodes: class methods.
b edges connectToAll: #dependentMethods.
b layout sugiyama.
].
b edges connectToAll: #subclasses.
b layout tree.
b

11.6 Visualizing File and Directories


File systems have a hierarchy driven by the nesting of folders and files. Con-
sider the following code:
fr := UIManager default chooseDirectory.
fr ifNil: [ ^ self ].
allChildren := fr allChildren.
allChildren := allChildren copyWithout: fr.

b := RTMondrian new.

b nodes: fr children forEach: [ :fileRef |


b shape box
size: [ :f | (f size + 1) log * 3 ].
b nodes: fileRef allChildren.
b edges connectFrom: #parent.
Visualizing File and Directories 139

Figure 11.10: Connecting nested colored elements.

Figure 11.11: Visualization of a file system.

b layout tree.

].
b layout verticalLine.
b normalizer
objects: allChildren;
distinctColorUsing: #extension.

The instruction UIManager default chooseDirectory opens a file browser to


let the user (i.e., you) select a directory. The class UIManager offers many
140 Visualizing Polymetric Graphs using Mondrian

methods to require input from the user. The message allChildren, sent to the
FileReference returned by chooseDirectory, returns a collection of all the file
references. For each file directly contained in the selected directory, a hierar-
chy is shown. The size of a box representing a file depends on the number of
characters contained in that file. A logarithmic scale is used to cope with file
size disparities. Thanks to the GT infrastructure, clicking on a file displays
its content.
Chapter 12

Charting, Plotting and


Curving using Grapher

Graphing, plotting, and curving data points is essential when analyzing data.
Roassal provides Grapher, an API implemented as a builder dedicated to
building interactive charts. A graph is represented as an instance of the class
RTGrapher. A set of data points is represented by an instance of the class
RTData. A graph is therefore built by adding data sets in a grapher object.
This chapter first covers the different charts supported by Grapher. Sub-
sequently, customization will be presented to fine-tune a chart.

12.1 Scatterplot
A scatterplot is a collection of Cartesian coordinates to display values for two
variables in a data set. A scatterplot is a common way to represent bi-variate
data.

Simple chart
Grapher offers a flexible way to draw scatterplots. Consider the following
example, to be evaluated in a playground (Figure 12.1):
b := RTGrapher new.
ds := RTData new.
ds dotShape color: Color blue.
ds points: { 4 @ 5 . 10 @ 5 . 3 @ 2 }.
ds x: #x.
ds y: #y.
b add: ds.
142 Charting, Plotting and Curving using Grapher

Figure 12.1: Simple scatterplot.

First, an instance of the class RTGrapher is created and assigned to the


b variable. A data set is then created by instantiating RTData. We give as
data point three points (we recall that 4 @ 5 corresponds to the point (4, 5)).
Each data point is represented as a blue dot. A point in Pharo answers to
the message x and y, which are specified using x: and y:. Each object data
point provided to a RTData is evaluated using the argument of x: and y: to
compute the corresponding numerical values. Finally, the data set is added
in the grapher.
A data set for which each element has to be plotted using two functions,
one against the X-axis and another against the Y-axis), is modeled using the
class RTData.
Consider this slightly more elaborated example that relate earthquake
depth and magnitude (Figure 12.2):
tab := RTTabTable new input: 'http://earthquake.usgs.gov/earthquakes/feed/v1.0/
summary/2.5_month.csv' asUrl retrieveContents usingDelimiter: $,.
tab removeFirstRow.
tab replaceEmptyValuesWith: '0' inColumns: #(4 5).
tab convertColumnsAsFloat: #(4 5).

b := RTGrapher new.
ds := RTData new.
ds interaction popup.
ds dotShape color: (Color blue trans).
ds points: tab values.
ds x: [ :row | row at: 4 ].
ds y: [ :row | row at: 5 ].
b add: ds.

b maxY: 8.
Scatterplot 143

Figure 12.2: Depth and magnitude of seisms greater than 2.5 during the last
30 days.

b maxX: 700.

b axisX title: 'depth'.


b axisY title: 'magnitude'.
b

The code above fetches CSV data from a server. Try to open the used web
address to see what the data looks like before processing it. After having
created the table RTTabTable, the first row, which contains the column names,
is removed since it is of no use in this example. Columns 4 (depth) and 5
(magnitude) are converted into float numbers. The x value is obtained by
fetching the 4th element of a CSV row, and the y value from the 5th element.
Note that the data set we use does not contain information about earthquake
with a magnitude below 2.5.
Here is another example that visualizes application source code (Figure
12.3):
methods := Collection withAllSubclasses flatCollect: #methods.

b := RTGrapher new.
ds := RTData new.
ds dotShape circle color: Color red trans.
ds points: methods.
ds x: #numberOfLinesOfCode.
ds y: [ :m | m getSource size ].
b add: ds.
b

The variable methods contains all the methods of the Pharo Collection
class hierarchy. Approximately 3,500 methods are defined in the collection
144 Charting, Plotting and Curving using Grapher

Figure 12.3: Source code visualization.

class hierarchy.
Each data point is represented as a circle using the default size of 5 pixels,
and colored with a translucent red. The collection of data points is specified
using points:.
The x-value and y-value have to be computed for each data point. The
number of line of code for a method is obtained with numberOfLinesOfCode.
The block [ :m | m getSource size ] returns the number of characters of the
method.
Figure 12.3 reveals an obvious correlation between the number of lines
of code and the number of characters of the method. Deviation from the
diagonal indicates methods with either very long or very short lines of code.

Multiple charts
Grapher supports different data sets to be simultaneously displayed. Con-
sider the following example (Figure 12.4):
methods := Collection withAllSubclasses flatCollect: #methods.
trachelMethods := TRObject withAllSubclasses flatCollect: #methods.

b := RTGrapher new.

"Data set 1"


ds1 := RTData new.
ds1 dotShape circle color: Color red trans.
Scatterplot 145

Figure 12.4: Two data sets in the same chart.

ds1 points: methods.


ds1 x: #numberOfLinesOfCode.
ds1 y: [ :m | m getSource size ].
b add: ds1.

"Data set 2"


ds2 := RTData new.
ds2 dotShape circle color: Color blue trans.
ds2 points: trachelMethods.
ds2 x: #numberOfLinesOfCode.
ds2 y: [ :m | m getSource size ].
b add: ds2.

b axisX title: 'Number of lines of code'.


b axisY title: 'Number of characters'.
b

The first data set, the methods of the Collection class hierarchy (methods),
is colored in red. The second data set, all the methods defined in Trachel
(trachelMethods), are colored in blue.

Axis configuration
By default, the X and Y axes have four ticks, and each tick is labeled with
a numerical value with a precision of one decimal point. Grapher however
offers a number of options for configuring the axes. For example, in our sit-
uation, both axes are labeled with integer values: the number of lines and
the size of method definitions are integer values. Consider the example seen
previously, for which only integer values with a comma as thousand separa-
tors:
...
"Axis configuration"
146 Charting, Plotting and Curving using Grapher

Figure 12.5: Axis configuration.

b axisX title: 'Number of lines of code'; noDecimal.


b axisY title: 'Number of characters'; withThousandsSeparator.
b

A number of options may be provided to an axis:

• title: to specify a title to the axis.

• noDecimal to not have decimal on the axis.

• noLabel to not have any label.

• twoDecimals to have two decimal numbers.

• withThousandsSeparator to have a thousand separation in the labels.

• rotateLabels to rotate the label by 45 degrees. Useful on the X-axis.

• decimal: to specify the number of decimal numbers to have.

• labelConversion: to transform the value being used in the axis. See the
section on translating the Y-axis.

The complete list of options to configure axes is given by the class RTAxis-
Configuration. Browse it for an overview of the different options.

12.2 Curve
A curve is obtained by connecting data points with a line.
Curve 147

Function
Consider the following script:
b := RTGrapher new.

ds := RTData new.
ds noDot.
ds points: (0 to: 3.1415 * 5 by: 0.01).
ds y: #sin.
ds x: #yourself.
ds connectColor: (Color red alpha: 0.4).
b add: ds.

ds := RTData new.
ds noDot.
ds points: (0 to: 3.1415 * 5 by: 0.01).
ds y: #cos.
ds x: #yourself.
ds connectColor: (Color blue alpha: 0.4).
b add: ds.
b

Note that #sin is rigorously equivalent to [ :v | v sin ], simply shorter.


Grapher represents each data point with a dot in the visualization. In
this example, the dots are not relevant. They are not part of the visualization,
thanks to the message noDot. Instead, the data points are linked to each other
using a connecting line, as defined by the new keyword here connectColor:.
A line is drawn between the invisible dots in the order they were provided
the points: keyword.

Stacking
Data points may be stacked, meaning that the X value is not computed, but
instead determined by the index in the collection. All the data points are
therefore equally horizontally distributed. The following example shows 4
data points horizontally ordered in the same order as they were provided to
points:, as seen in Figure 12.7.

b := RTGrapher new.

ds := RTData new.
ds dotShape color: Color red.
ds points: #(5 1 20 5).
ds y: #yourself.
b add: ds.
148 Charting, Plotting and Curving using Grapher

Figure 12.6: Curves defined as functions.

Figure 12.7: Simple stack of data points.

b axisX noLabel; noTick.


b axisY noDecimal.
b

A slightly more elaborate example is given below. Each curve is a class


contained in Roassal. Each data point is a method, sorted along their size,
in a reverse order. The Y-value of a method is its size in number of lines of
code.
classes := RTShape withAllSubclasses.

b := RTGrapher new.
b extent: 400 @ 200.

normalizer := RTMultiLinearColorForIdentity new objects: classes.


Curve 149

Figure 12.8: Stacking data points.

classes do: [ :c |
ds := RTData new.
ds dotShape rectangle color: (normalizer rtValue: c) trans.
ds points: (c methods reverseSortedAs: #numberOfLinesOfCode ).
ds interaction popup.
ds connectColor: (normalizer rtValue: c) trans.
ds y: #numberOfLinesOfCode.
b add: ds.
].
b

Figure 12.8 shows 27 different curves, each representing a subclass of the


class RTShape.
A distinct color is given to each curve. This is useful for differentiating
classes. To achieve this, a RTMultiLinearColorForIdentity object has to be ini-
tialized with the objects that will be colored. The message objects: is used
for that purpose. The expression (normalizer rtValue: c) returns a color that is
specific for the argument c.

Stacking or not stacking a data set


So far, a data set has been presented as a list of objects and two metrics spec-
ifying using x: and y:. The two values for each data point are then computed
using the metrics.
A stacked data set is a data set for which only the list of objects and the y:
metric are provided. The X value is determined using the list order.
A scatterplot and a bar chart differently visualizes a data set. A bar chart
needs a list of values to be represented (e.g., 5, 6, 7). A scatterplot needs a list
150 Charting, Plotting and Curving using Grapher

Figure 12.9: Not stacked data set.

of two coordinates (e.g., 2 @ 3, 5 @ 6).


Consider the following script:
b := RTGrapher new.

ds := RTData new.
ds dotShape size: 8; color: Color red.
ds points: #(5 6 7).
ds x: [ :v | v ].
ds y: [ :v | v * v ].
b add: ds.
b

In Figure 12.9, each point has both its X and Y values that are computed.
Consider this slightly modified version:
b := RTGrapher new.

ds := RTData new.
ds dotShape size: 8; color: Color red.
ds points: #(5 6 7).
ds y: [ :v | v * v ].
b add: ds.

b axisX noTick; noLabel.


b

In Figure 12.10, each point has both its Y value that is computed. The X
value is determined from the order of the objects provided to points:. Stacked
data sets are useful in bar charts, as in the following example:
b := RTGrapher new.

ds := RTData new.
ds barShape color: Color red.
ds points: #(5 6 7).
Curve 151

Figure 12.10: Stacked data set.

Figure 12.11: Stacked data set.

ds y: [ :v | v * v ].
b add: ds.

b axisX noTick; noLabel.


b

Figure 12.10 shows the result of the previous script. Bars are used instead
of dots.

Stacking multiple curves


Consider two data sets #(1 1 4 6) and #(2 4 2 10 5 2) (Figure 12.12):
points1 := #(1 1 4 6).
points2 := #(2 4 2 10 5 2).

b := RTGrapher new.

ds := RTData new.
ds points: points1.
ds connectColor: Color blue.
ds y: #yourself.
b add: ds.
152 Charting, Plotting and Curving using Grapher

Figure 12.12: Stacking multiple curves.

ds := RTData new.
ds points: points2.
ds connectColor: Color green.
ds y: #yourself.
b add: ds.

b axisX noLabel; noTick.


b

The visual aspects of data points may be customized. Consider the fol-
lowing example.

b := RTGrapher new.

ds := RTData new.
ds dotShape rectangle color: Color red.
ds points: (RTShape withAllSubclasses sortedAs: #ageInDays).
ds y: [ :c | c ageInDays ].
b add: ds.

ds := RTData new.
ds dotShape circle color: Color blue.
ds points: (TRShape withAllSubclasses sortedAs: #ageInDays).
ds y: [ :c | c ageInDays ].
b add: ds.

b axisX noDecimal; title: 'classes'.


b axisY noDecimal; title: 'age in days'.
b

Figure 12.13 represents the age of shape classes contained in Trachel (i.e.,
subclasses of the class TRShape) and Roassal (i.e., subclasses of the class
RTShape).
Labeled bar chart 153

Figure 12.13: Data point aspect.

Figure 12.14: Simple bar chart.

12.3 Labeled bar chart


Labels may be added in a bar chart. The instruction barChartWithBarTitle: al-
lows for specifying a bar name computed from the data point. Consider the
following example: (Figure 12.14):
b := RTGrapher new.
ds := RTData new.
ds barShape color: Color red.
ds points: #(-5 -9 10 -2).
ds barChartWithBarTitle: [ :value | '##', value asString ].
b add: ds.
b axisX noLabel; noTick.
b

The angle of rotation may be set using barChartWithBarTitle:rotation:. Rotat-


ing labels is useful for avoiding overlapping labels.
154 Charting, Plotting and Curving using Grapher

Figure 12.15: Centered bar chart labels.

In some situations, labels have to be centered. One could use barChartWith-


BarCenteredTitle: in that case (Figure 12.15):

b := RTGrapher new.
ds := RTData new.
ds barShape color: Color red.
ds points: #(-5 -9 10 -2).
ds barChartWithBarCenteredTitle: [ :value | '##', value asString ].
b add: ds.
b axisX noLabel; noTick.
b

Bar may be colored depending on a specific value. Consider the follow-


ing example (Figure 12.16):
b := RTGrapher new.
ds := RTData new.
ds barShape
if: [:v | v < 0 ] fillColor: Color red;
if: [:v | v > 0 ] fillColor: Color green.
ds points: #(5 -6 10 20 -9).
b add: ds.
b axisX noLabel; noTick.
b axisY noDecimal.
b

12.4 Interaction
Interactions may be defined to get particular behavior upon user actions. A
typical case is getting a data point when the mouse is located above it. Per
default, all the data points have the popup activated.
Interaction 155

Figure 12.16: Coloring bars.

Sophisticated popup actions may give details about the pointed element.
Consider the following code (Figure 12.17):

b := RTGrapher new.

ds := RTData new.
ds barShape color: Color veryLightGray.
ds points: (RTShape withAllSubclasses).
ds y: [ :c | c methods size ].
ds interaction highlightColored: Color red.

ds interaction popup
named: [ :c | c name, ' methods' ]
background: Color lightBlue
group: [ :group :element |
| s ms |
s := RTBox new size: #numberOfLinesOfCode; color: Color red.
ms := element model methods sortedAs: #numberOfLinesOfCode.
group addAll: (s elementsOn: ms).
RTGridLayout on: group ].

b add: ds.

b axisX noTick; noLabel.


b

Two interactions are defined on each bar. First, the bar on which the
mouse is above is red. The original bar color is restored when the mouse
leaves the bar. The second interaction is a grouped popup, which has a name,
a background color, and a set of method elements. The size of a method
reflects the number of lines of code defining the method.
Note that this interaction is available for all kinds of charts.
156 Charting, Plotting and Curving using Grapher

Figure 12.17: Interaction in a bar chart.

Figure 12.18: Horizontal line average.

12.5 Decoration
A chart often needs to be decorated, e.g., labeling particular values, adding
an average, threshold. Grapher supports such operations thanks to a dedi-
cated class hierarchy. All subclasses of RTAbstractGrapherDecorator describes
a particular decorator.
For example, a line indicating average may be added using (Figure 12.18):
b := RTGrapher new.
ds := RTData new.
ds dotShape color: Color red.
ds points: #(5 1 20 8).
b add: ds.
b addDecorator: (RTAverageDecorator new withLabel;
labelConvertion: [ :aValue | 'average = ', aValue asFloat asString ]).
b
Translating the Y axis 157

Figure 12.19: Horizontal line average.

RTCursorFollower is a handy decoration that adds lines following the


mouse cursor. Here is an example (Figure 12.19):
b := RTGrapher new.

ds := RTData new.
ds points: #(5 10 6 2 -2.5).
b add: ds.

b addDecorator: (RTCursorFollower new color: Color blue).


b

Color and the label computation may be parametrized. For example, the
example above sets a color to the following lines and labels. The class RTCur-
sorFollower offers the following configuration methods:

• color: sets a color to the lines and labels

• labelXTextConvertion: and labelYTextConvertion: allows for converting the


text located along the cursor lines. Per default, the labels are set to
display only two digits ([ :v | v round: 2 ]).

12.6 Translating the Y axis


Although it is usually not advised to have the label crossing at a different
point than 0 @ 0, axis translation is supported in Grapher. Consider the
following example:
b := RTGrapher new.
158 Charting, Plotting and Curving using Grapher

Figure 12.20: Example without Y-Axis translation.

d := RTData new.
d points: (0 to: 3.14 * 2 by: 0.05).
f := [ :x | x sin + 4 ].
d y: [ :x | f value: x ].
d x: #yourself.
b add: d.

The translation along the Y-axis is realized by modifying the function


provided to y: and using a labelTranslation:. Consider a new version of the
script given above (Figure 12.21):

b := RTGrapher new.

d := RTData new.
d points: (0 to: 3.14 * 2 by: 0.05).
f := [ :x | x sin + 4 ].
d y: [ :x | (f value: x) - 4 ].
d x: #yourself.
b add: d.
b axisY
labelConversion: [ :y | y + 4 ].
b

Translating the X-axis to Y = 4 is done by subtracting 4 to the function


provided to y: and summing 4 to the function provided to labelConversion:.
Date on the axis 159

Figure 12.21: Example with Y-Axis translation.

12.7 Date on the axis


Dates are particular values that require an adequate control over what is
being displayed on the X-axis. This is enabled using the julianDayNumber
message on a date object, converting it into a number.
Consider the following example that shows the creation of methods
along time (Figure 12.22):
methods := RTObject withAllSubclasses flatCollect: #methods.
methods := methods reject: [ :m | m numberOfLinesOfCode > 150 ].
oldestMethod := methods minFor: #date.

b := RTGrapher new.

ds := RTData new.
ds dotShape circle size: 5; color: (Color blue alpha: 0.1).
ds interaction popup.
ds points: methods.
ds y: #numberOfLinesOfCode.
ds x: [ :m | m date julianDayNumber - oldestMethod date julianDayNumber ].
b add: ds.

b axisY title: 'LOC'; noDecimal.

b axisX
title: '';
labelRotation: -30;
numberOfTicks: 10;
numberOfLabels: 10;
labelConversion: [ :v | (Date julianDayNumber: v + oldestMethod date
julianDayNumber) ].
b
160 Charting, Plotting and Curving using Grapher

Figure 12.22: Date on the X-axis.

12.8 Several metrics per data point

The class RTMultipleData provides a way to handle more than one metric per
data point, represented by grouped bars. Consider the following example
(Figure 12.23):

b := RTGrapher new.

d := RTMultipleData new.
d barShape color: Color blue.
d points: #( #('hello' 1 2 1) #('world' 2 4 2) #('bonjour' 3 5 4) #('Gutten Morgen' -1
4 -5)).
d addMetric: #second.
d addMetric: #third.
d addMetric: #fourth.

"Rotated text"
d barChartWithBarTitle: #first rotation: -30.

b add: d.

There are two ways for doing naming groups of bars. On the example
above labels begins on the central element of each group and is rotated. An
alternative is to use barChartWithBarCenteredTitle: which use a horizontal la-
bel.
Adding a legend 161

Figure 12.23: Multipoint example.

12.9 Adding a legend

A legend often plays a significant role in the adoption of a visualization. Gra-


pher supports a simple and expressive way to add a legend. Each RTData
can have a title. Sending the message legend to the grapher returns a legend
builder (described in the following chapters).
Consider the following example (Figure 12.24):

methods := Collection withAllSubclasses flatCollect: #methods.


trachelMethods := TRObject withAllSubclasses flatCollect: #methods.

b := RTGrapher new.

"Data set 1"


ds1 := RTData new.
ds1 label: 'collection methods'.
ds1 dotShape circle color: Color red trans.
ds1 points: methods.
ds1 x: #numberOfLinesOfCode.
ds1 y: [ :m | m getSource size ].
b add: ds1.

"Data set 2"


ds2 := RTData new.
ds2 label: 'trachel methods'.
ds2 dotShape circle color: Color blue trans.
ds2 points: trachelMethods.
ds2 x: #numberOfLinesOfCode.
ds2 y: [ :m | m getSource size ].
b add: ds2.
162 Charting, Plotting and Curving using Grapher

Figure 12.24: A graph with a legend.

b axisX title: 'Number of lines of code'; noDecimal.


b axisY title: 'Number of characters'; noDecimal.

"We build the legend, and indicate it has to be located below"


b legend below.

The legend summarizes the color and the title of each data set.
Chapter 13

Visualization Composition

As we have seen previously, a builder models user input and represents it by


building a RTView. Visualizations produced by builders may be composed
with other visualizations. This is particularly useful when two visualizations
may be "glued" together to form a complex one.
In essence, the composition of several builders is achieved by sharing a
unique view among these builders and properly ordering elements added
by each view. The class RTComposer specifically fulfills that purpose.

13.1 Composing builders


Consider the following example (Figure 13.1):
data := #(20 30 50 100).

c := RTComposer new.

"First visualization"
g := RTGrapher new.
g extent: 200 @ 200.
g view: c view.
ds := RTData new.
ds interaction popup.
ds barShape color: Color blue.
ds points: data.
g add: ds.
g axisX noTick; noLabel.
g build.
c group: #graph.

"Second visualization"
164 Visualization Composition

Figure 13.1: Composition of two visualizations.

b := RTPieBuilder new.
b view: c view.
b interaction popup.
b objects: data.
b slice: #yourself.
b labeled.
b build.
c group: #pie.

"Layouting"
c move: #graph onTheLeftOf: #pie.

RTMetricNormalizer new
view: c view;
objects: data;
distinctColor.

c view

The variable data contains four numbers and represents the input of the
script. Two visualizations are composed, using RTGrapher and RTPieBuilder.
The variable c represents a composer. The two visualizations are composed
of:

• Sharing the view to both builders. This is carried out with g view: c view
and b view: c view

• After invoking build on a builder, the composer needs to snapshot the


elements and give a name. The expression c group: #graph gives the
name #graph to the elements added by RTGrapher. Similarly, elements
added by RTMondrian are named #mondrian.

• Adequately positioning elements produced by the builders. The ex-


pression c move: #graph onTheLeftOf: #mondrian performs this operation.
Propagating events 165

13.2 Propagating events


Events may be propagated from one builder to another. The method propa-
gateHighlight defined on the class RTComposer serves this purpose. Consider
a slightly different version of the previous script.
data := #(20 30 50 100).

c := RTComposer new.

"First visualization"
g := RTGrapher new.
...
g build.
c group: #graph.

"Second visualization"
b := RTPieBuilder new.
...
b build.
c group: #pie.

"Layouting"
c move: #graph onTheLeftOf: #pie.
c propagateHighlightToAll.

RTMetricNormalizer new
view: c view;
objects: data;
distinctColor.

c view

In total, four bars and four squares are represented. Each group contains
the model given by the variable data. Using propagateHighlightToAll highlights
elements in the view that have the same model. This is useful when elements
have to be globally highlighted.

13.3 Titled visualization


It often happens that visualizations have to be given a title to make
the overall picture meaningful. The class RTComposer offers the method
nameGroup:as: to set a title to element groups. Consider the extension of
the previous script (Figure 13.2):
...
166 Visualization Composition

Figure 13.2: Giving a title to a group of elements.

"Layouting"
c move: #graph onTheLeftOf: #pie.

c nameGroup: #graph as: 'Values as a bar chart'.


c nameGroup: #pie as: 'Values as pie areas'.

RTMetricNormalizer new
view: c view;
objects: data;
distinctColor.

c view

13.4 World population Example


The following example gives two representation of the World population.
Using a geographical map and a bar chart (Figure 13.3):
composer := RTComposer new.
v := composer view.

"========"
b := RTMapBuilder new.
b view: v.

countries := RTMapBuilder countriesPopulation select: [ :c | RTSVGPath


countries includes: (c at: 1) ].

cn := RTNColorLinearNormalizer
inContext: (countries collect: [:c | c at: 2])
lowColor: (Color r:0.8 g:0.8 b:1)
highColor: (Color r:0 g:0 b:0.3).
World population Example 167

b countries: countries named: [ :c | c at: 1 ].


b color: [ :dic | cn rtValue: (dic at: 2) ].
b withPopup: [ :c | (c at: 1) asString, ': ',(((c at: 2) / 1000000 asFloat round: 3)
asString, ' Million') ].
b build.
composer group: #worldMap.

"========"
grapher := RTGrapher new.
grapher extent: 600 @ 300.
grapher view: v.
ds := RTData new.
ds barShape width: 5; color: Color blue.
ds points: ((countries reverseSortedAs: #second) copyFrom: 1 to: 50).
ds y: #second.
ds interaction popupText.
grapher add: ds.
grapher axisX noLabel; noTick; title: 'countries'.
grapher axisY noDecimal; labelConversion: [ :aValue | (aValue / 1000000) round:
2]; title: 'Millions'.
grapher build.
composer group: #graph.

"========"
composer move: #graph above: #worldMap.

composer nameGroup: #worldMap as: 'Human distribution across the globe'.


composer nameGroup: #graph as: 'Chart'.
composer propagateHighlightToAll.
v
168 Visualization Composition

Figure 13.3: Two representations of the world population.


Part III

Applications
Chapter 14

Documenting with a Legend

A legend is essential as soon as non-trivial data is visualized. Unless a vi-


sualization is frequently used or highly intuitive, a legend is necessary to
guide the user with all the different visual cues. RTLegendBuilder is the main
class to build legends. It offers facilities that are both easy to use and flexible
enough to accomodate most of the need.
A legend is created with the following steps:

1. Create an instance of the class RTLegendBuilder


2. A view has to be provided to the legend builder. The legend will be
added to that view.
3. The legend is defined using the multiple utility functions (e.g., addText:,
addColor:text:)

4. A position may be optionally provided (e.g., below, above, onDemand).


5. The message build has to be sent to the builder

14.1 A first example


As a first example, consider the following code snippet that simply visu-
alizes some classes. The visualization uses a color to indicate a particular
threshold on the color of each class. The size of a class reflects the number of
methods of the class, and if the number of lines of code of the class is below
1,000 then the class is green, otherwise it is red (Figure 14.1):
"The view will be passed to the RTLegendBuilder"
v := RTView new.
172 Documenting with a Legend

Figure 14.1: Simple use of a legend.

es := RTBox new
size: #numberOfMethods;
color: [ :cls |
cls numberOfLinesOfCode <= 1000
ifTrue: [ Color green ]
ifFalse: [ Color red ] ];
elementsOn: Collection withAllSubclasses.
v addAll: es.
RTFlowLayout on: es.

"We add a legend"


lb := RTLegendBuilder new.
lb onDemand.
lb view: v.
lb addText: 'Each box is a class, belonging to the Collection class hierarchy'.
lb addColor: Color green text: 'Class with less than 1000 lines of code'.
lb addColor: Color red text: 'Class with more than 1000 lines of code'.
lb build.

The first code given above creates a small visualization and adds a legend
to it. The legend is available on-demand, meaning that the user has to locate
the mouse cursor above the ? character, located on the top-left corner of the
visualization. The view, referenced by the variable v, has to be passed to the
legend builder. The legend then has to be configured. The message addText:
provides a descriptive text of the legend and addColor:text: is used to give a
Building a legend 173

meaning to a color. The message build adds the legend in the view v.

14.2 Building a legend


The class RTLegendBuilder offers many utility methods to build expressive
legends. In particular:

• addText: adds a text to the legend


• addColor: aColor text: aText provides a textual description of a color
• addColorFadingFrom: startColor to: endColor text: textualDescription useful
for describing a color fading ranging from startColor to endColor
• addColorFadingUsing: colors text: textualDescription describes a fading
based on the provided set of colors.
• addLineColor: aColor text: aText associates a text to a colored line
• addRectanglePolymetricWidth: widthDescription height: heightDescription
box: boxDescription associates a description to the height, width, and
the color of a box

14.3 Documenting a polymetric shape


Clearly indicating the meaning of the different visual dimensions is highly
important to make the visualization accessible. The following example adds
a legend to a simple visualization of some classes (Figure 14.2):
b := RTMondrian new.

b nodes: Collection withAllSubclasses.


b edges moveBehind; connectFrom: #superclass.
b layout cluster.
b normalizer
normalizeColor: #numberOfLinesOfCode;
normalizeWidth: [ :cls | cls numberOfInstanceVariables * 5 ];
normalizeHeight: #numberOfMethods.
b build.

lb := RTLegendBuilder new.
lb view: b view.
lb addRectanglePolymetricWidth: 'number of instance variables' height: 'number
of methods' box: ''.
lb addColorFadingFrom: Color gray to: Color red text: 'Number of lines of code'.
174 Documenting with a Legend

Figure 14.2: Documenting a polymetric shape.

lb addLineColor: Color gray text: 'superclass relation'.


lb build.

b view

A description of each metric is provided using the message addRectangle-


PolymetricWidth:height:box: .
Chapter 15

Expressing Epidemiological
Models

Understanding how infectious diseases propagate is a key challenge for


the 21st century. Mathematical modeling is a powerful tool for studying
complex systems that are commonly used in many scientific disciplines. It
is widely used to model infectious diseases in order to study the mecha-
nisms of transmission, explore characteristics of epidemics, predict the fu-
ture course of an outbreak and evaluate strategies to find a best control-
program like quarantine. The first mathematical model of epidemiology was
proposed by Daniel Bernoulli in 1766 to defend the practice of inoculation
against smallpox. The major contribution to modern mathematical epidemi-
ology was carried out by Kermack and McKendrick who formulated a com-
partmental model based on relatively simple assumptions on the rates of
flow between different classes categorized by epidemiological status.
Kendrick is an embedded domain-specific language in Pharo for defining
mathematical models of epidemiology. This chapter analyzes and visualizes
the spatiotemporal evolution of epidemiological models using Roassal.
This chapter was written with the participation of Bui Thi Mai Anh (ma-
[email protected]), Serge Stinckwich ([email protected]), and Nick
Papoulias ([email protected]).

15.1 Compartmental models of epidemiology


The targeted models of the Kendrick language are the SIR or SEIR compart-
mental model in which each individual goes through three distinct phases:

• First an individual is considered to be Susceptible to pathogens (status


176 Expressing Epidemiological Models

Figure 15.1: Mathematical description of SIR model using ODEs.

S),
• Once infected, the individual is Infectious (status I) and spreads the in-
fection,
• After Recovery (status R), the individual is immunized and cannot be-
come infected again.

The transition of status between compartments is represented mathemat-


ically as derivatives of compartment size with respect to time.
At the moment, Kendrick supports for the mathematical models of epi-
demiology based on ordinary differential equations (ODEs). The system of
ODEs followed represents the SIR classic model of epidemiology (see figure
15.1).
These models are specified using the Kendrick domain-specific language
and modeled using the simulation modules integrated into Kendrick plat-
form. The simulator takes a Kendrick model (i.e., an epidemiological model
written in the Kendrick language) and performs a simulation algorithm and
outputs the result showing the spatial and temporal evolution dynamics of
each compartment. Roassal visualizes the result.
The simulation module supports three modeling formalisms: determinis-
tic, stochastic and individual-based (also called agent-based). The modelers
can switch between the simulation modes by indicating the algorithm used.
The deterministic simulation resolves the ODEs system of the model
and produces numerical results. Kendrick uses deterministic solvers imple-
mented in the package PolyMath, including RungeKutta and Euler. These
solving methods are used to find numerical approximations to the solutions
of ODEs. The results are always the same for all simulations under the pre-
defined model parameters and initial conditions (initial values of compart-
ments). Therefore, deterministic models reflect the average dynamics of the
disease.
While deterministic models provide insights into the endemic equilib-
rium and its stability, shifting to stochastic models is known to be more re-
Visualizing Ebola epidemic outbreak from data 177

alistic in unrderstanding and predicting the dynamics of diseases. Kendrick


currently supports some Simulation Stochastic Algorithms (SSA) of Gillespie
- converting the ODEs system of the model to stochastic events. After each
time step, an event is randomly choosen to be executed. Kendrick allows one
to increase the model accuracy using an individual-based simulator. Every
individual of the population is examined at each time step and each individ-
ual has the probability of changing its current status.
The numerical results produced by simulations can be displayed graphi-
cally by using visualization modules of Kendrick. Such modules are imple-
mented based on some features of Roassal.
There are three kinds of visualizations: diagrams (using RTGrapher of
Roassal), maps (using RTMapBuilder) and networks (using RTMondrian).

15.2 Visualizing Ebola epidemic outbreak from


data
The website https://github.com/cmrivers/ebola provides data that represents the
2014 Ebola outbreak in West African countries. The following script visual-
izes data for Guinea and Liberia (Figure 15.2):
tab := RTTabTable new input: (ZnEasy get:
'https://raw.githubusercontent.com/cmrivers/ebola/master/country_timeseries.csv')
contents usingDelimiter: $,.
tab removeFirstRow.
tab replaceEmptyValuesWith: '0' inColumns: (2 to: 12) asArray.
tab convertColumnsAsInteger: (2 to: 12) asArray.
tab convertColumnAsDateAndTime: 1.
tab replaceZeroWithCumulativeValuesInColumns: (2 to: 12) asArray.
data := tab values reversed.

minValue := data minValue: [ :aData | aData first julianDayNumber ].

b := RTGrapher new.
b extent: 400@200.
ds := RTData new.
ds noDot.
ds connectColor: Color blue.
ds points: data.
ds y: [ :v | v at: 3 ].
ds x: [ :v | v first julianDayNumber - minValue ].
b add: ds.

ds := RTData new.
ds noDot.
ds connectColor: Color green.
178 Expressing Epidemiological Models

Figure 15.2: Visualizing Ebola cases in two African countries.

ds points: data.
ds y: [ :v | v at: 4 ].
ds x: [ :v | v first julianDayNumber - minValue ].
b add: ds.

b axisX
labelRotation: -30;
labelConversion: [ :v | (Date julianDayNumber: v + minValue) ].
b build.

lb := RTLegendBuilder new.
lb view: b view.
lb addText: 'Ebola cases'.
lb addColor: Color blue text: 'Guinea'.
lb addColor: Color green text: 'Liberia'.
lb build.
b view

15.3 Loading Kendrick

Kendrick is loadable using a Roassal plugin (Figure 15.3):


Measles model 179

Figure 15.3: Loading Kendrick.

15.4 Measles model


Measles is a well-known childhood infectious disease. The most appropriate
model representing measles epidemics is the SEIR model where individuals
are categorized to be in four classes: first, all newborn individuals are as-
sumed to be in the Susceptible (S) class at birth rate µ, then enter in Exposed (E)
class who are infected but not yet infectious with transmission rate β, then
become Infectious (I) after a latent period given by 1/σ, and finally change to
Recovery after an infectious period 1/γ. The parameter N represents the total
size of the population. In this example, the diagrams will be used to visu-
alize the dynamics of the infectious disease. The following script illustrates
the SEIR model of measles (Figure 15.4):
model := KEModel new population: (KEPopulation size: 100000).
model addAttribute: #status value: #(S E I R).
model atCompartment: { #status->#S } put: 99999 atOthersPut: 0.
model atCompartment: { #status->#I } put: 1.
model addEquations: {
'S:t=mu*N - beta*S*I - mu*S'.
'E:t=beta*S*I - sigma*E - mu*E'.
'I:t=sigma*E - gamma*I - mu*I'.
'R:t=gamma*I - mu*R'
}.
model addParameters: {
#beta->0.0000214.
#gamma->0.143.
#mu->0.0000351.
#sigma->0.125 }.
simulator := KESimulator new: #RungeKutta from: 0.0 to: 150 step: 1.
simulator executeOn: model.
180 Expressing Epidemiological Models

Figure 15.4: Modeling the Measles model with Kendrick.

diag := (KEDiagramBuilder new) data: simulator allTimeSeries.


diag xLabel: 'Time (days)'.
diag open

The rates mentioned of the model are introduced as parameters. This


model is run in deterministic simulation in a period of 150 days (using the
classical Runge–Kutta method, also known as RK4) with an initial popula-
tion of 100000 individuals. Figure 15.4 shows the temporal dynamics of all
compartments S, E, I and R. The Y axis counts the number of individuals in
each class and the X axis is the time period of simulation (in days).
The figure shows that after about 37 days, the endemic reaches the peak,
then the number of infectious reduces, and finally the number of recovered
individuals increases.
Complex models in Kendrick 181

Figure 15.5: Mathematical description of the multi-hosts model using ODEs.

15.5 Complex models in Kendrick


The modeling of infectious disease involves not only the study of their trans-
mission cycle (as shown in the previous example) but also the effects of the
population heterogeneities due to age intervals, sexes, species, viral strains
or spatial regions etc. We consider such heterogeneities as concerns of epi-
demiology. The Kendrick DSL allows one to construct a model as a com-
position of modular models that describe specific epidemiological concerns.
Each concern is separately defined and then integrated into the model. Look
at the following examples.

15.6 Example of multi-host model


In many situations, due to the fact that the primary hosts do not interact di-
rectly to transmit infection, the spreading of diseases requires a secondary
host (vector-borne diseases). In this example, we investigate the mosquito-
borne disease with two sources. Here, the matrix of transmission beta indi-
cates that the disease is only transmitted between mosquitos and two reser-
voirs, so that the main diagonal of this matrix is zero. A reservoir is the
long-term host of a pathogen of an infectious disease. Example of natural
reservoirs for pathogens are: rats or bats. This disease is mathematically
formulated as shown in figure 15.5.
We specify the multi-host aspect of epidemiology as a concern (called
multi-host concern) and then apply it to the model.
The Kendrick model of this disease is specified as follows:
multiHostConcern := KEConcern new.
multiHostConcern
addAttribute: #species
value: #(#mosquito #reservoir1 #reservoir2).

model := KEModel new.


model population: (KEPopulation size: 13000).
182 Expressing Epidemiological Models

model addAttribute: #status value: #(#S #I #R).


model addParameters: {
#beta -> 1.
#gamma -> 52.
#mu -> 12.17.
}.
model addParameter: #lambda value: 'beta*I'.
model addEquations: {
'S:t=mu*N - lambda*S - mu*S'.
'I:t=lambda*S - gamma*I - mu*I'.
'R:t=gamma*I - mu*R'
}.

model integrate: multiHostConcern.

model
atParameter: #mu
assignValue:
[ :aModel| |c val|
c := aModel currentCompartment at: #species.
c = #mosquito ifTrue: [ val := 12.17 ].
c = #reservoir1 ifTrue: [ val := 0.05 ].
c = #reservoir2 ifTrue: [ val := 0.05 ].
val
].
model
atParameter: #N
assignValue:
[ :aModel| |c|
c := aModel currentCompartment at: #species.
aModel sizeOfPopulation: c
].
model
atParameter: #beta
assignValue:
[ :aModel| |c val|
c := aModel currentCompartment at: #species.
c = #mosquito ifTrue: [ val := #(0 0.02 0.02) ].
c = #reservoir1 ifTrue: [ val := #(0.02 0 0) ].
c = #reservoir2 ifTrue: [ val := #(0.02 0 0) ].
val
].
model
atParameter: #lambda
assignValue:
[ :aModel|
((aModel atParameter: #beta) *
(aModel atCompartment: {#status->#I})) sum
].
An example of Ebola spatial model with Kendrick 183

model atCompartment: { #status->#S. #species->#mosquito } put: 9999.


model atCompartment: { #status->#I. #species->#mosquito } put: 1.
model atCompartment: { #status->#S. #species->#reservoir1 } put: 1000.
model atCompartment: { #status->#S. #species->#reservoir2 } put: 2000.

simulator := KESimulator new: #Gillespie from: 0.0 to: 0.5 step: 0.0027.
simulator executeOn: model.
db := (KEDiagramBuilder new) data: ((simulator timeSeriesAt: '{#status: #I}') sqrt).

db xLabel: 'Time (year)'.


db yLabel: 'sqrt(Infectious)'.
db open

In this example, we specify the multi-host concern separately from the


SIR model. This concern contains no parameters. Its attribute species con-
tains three values for mosquito, reservoir1, reservoir2. When integrating
this concern to the SIR model, the population is further decomposed in 3x3
compartments. Because of the heterogeneities caused by the multi-host con-
cern, parameters of SIR model such as β, µ, λ, N (N by default is the size
of the population) become heterogeneous. For example, according to the
species, the parameter µ may have different values (12.17 for mosquito, 0.05
for reservoir1 and reservoir2). Such parameters will be re-defined as shown
in the script (by re-assigning values).
In this example, we demonstrate the use of stochastic simulation (by in-
dicating the used algorithm as #Gillespie). The temporal evolution of this
disease is shown in the Figure 15.6. The diagram illustrates the dynamics
of the infectious compartment for each species. The Y-axis is squared for
readability.

15.7 An example of Ebola spatial model with


Kendrick
This example is used for demonstrating the map visualization of Kendrick.
We will study how a spatial concern is defined with Kendrick.
The example below describes the Ebola model in six countries of Africa.
Due to the characteristic of the Ebola epidemic, the infection transmission
only occurs via direct contact between the Susceptibles and an Infectious.
Hence, we consider that the spatial affects the transmission of infection by
the migration of infectious individuals from a country to another country.
In this example, we specify two concerns separately: the spatial concern
and the SIR concern, then apply them to the model. The script of the model
is below:
184 Expressing Epidemiological Models

Figure 15.6: Modeling Mosquito-borne model with Kendrick.

model := KEModel new population: (KEPopulation size: 6000).

map := KEMap new.


map countries: #(#Liberia #Guinea #SierraLeone #Nigeria #Senegal #Niger).
map routesFrom: #Liberia toAll: #(#Guinea).
map routesFrom: #Guinea toAll: #(#SierraLeone).
map routesFrom: #SierraLeone toAll: #(#Nigeria).
map routesFrom: #Nigeria toAll: #(#Senegal).
map routesFrom: #Senegal toAll: #(#Niger).
map routesFrom: #Niger toAll: #(Liberia).
spatialConcern := KEConcern new.
spatialConcern addAttribute: #country value: map countries.
spatialConcern addParameter: #rho value: 0.05.
spatialConcern transitions: (map routesToTransitions: 'rho').

sirConcern := KEConcern new.


sirConcern addAttribute: #status value: #(S I R).
sirConcern addParameters: #(#lambda #beta #gamma).
sirConcern
addTransitionFrom: { #status->#S }
An example of Ebola spatial model with Kendrick 185

to: { #status->#I }
probability: 'lambda'.
sirConcern
addTransitionFrom: { #status->#I }
to: { #status->#R }
probability: 'gamma'.

model integrate: sirConcern.


model integrate: spatialConcern.

model
atParameter: #beta
assignValue: 0.0002.
model
atParameter: #gamma
assignValue: 0.1.
model
atParameter: #lambda
assignValue:
[ :aModel| |c|
c := aModel currentCompartment at: #country.
(aModel atParameter: #beta)*(aModel atCompartment: {#status->#I. #country-
>c}) ].

#(#Guinea #SierraLeone #Nigeria #Senegal #Niger) do: [ :c|


(model atCompartment: { #status->#S. #country->c } put: 1000)
].
model atCompartment: { #status->#S. #country->#Liberia } put: 950.
model atCompartment: { #status->#I. #country->#Liberia } put: 50.

simulator := KESimulator new: #IBM from: 0 to: 100 step: 0.1.


simulator executeOn: model.

mapBuilder := KEMapBuilder africa.


mapBuilder
data:
(mapBuilder countries collect: [:c|
((model atAttribute: #country) includes: c)
ifTrue: [
(simulator timeSeriesAt: {#status->#I. #country->c}) first peakOfEpidemic ]
ifFalse: [ 0 ]
]).
mapBuilder open.
diagBuilder := KEDiagramBuilder new data: (simulator timeSeriesAt: {#status->#I}).
diagBuilder open.

Figure 15.7 represents the results of the individual-based simulation on


six countries. The left hand diagram shows the evolution dynamics during
186 Expressing Epidemiological Models

Figure 15.7: Representing the Ebola disease with Kendrick in 6 countries of


Africa.

100 days. The right hand graph visualizes six countries in Africa, the color
of each one depends on its infection degree.

15.8 Network Visualization of Epidemiological


Models
In all examples above, individuals of the population are assumed to mix
randomly and uniformly. Each individual therefore has an equal probability
to have contact with all other individuals. But in fact, the number of contacts
an individual has is considerably smaller than the size of the population. The
network model supposes a network of contacts between individuals of the
population. It assigns to each individual a finite set of contacts. Thus, the
probability of infection of a susceptible individual depends on the number
of infectious contacts he or she has.
Models that incorporate network structure of epidemiology are captured
by Kendrick as a spatial concern. The network is represented through graphs
in which a node represents an individual (or a set of individuals having the
same contacts) and an edge between two nodes represents an interaction
that may allow disease transmission (contact). Constructing a real contact
network required knowledge of every individual in a population and every
disease-causing contact between them. This is typically unfeasible for even
small populations. Thus people often work with approximate networks, par-
ticularly computer-generated networks. Kendrick supports some computer-
generated networks that have been investigated in the domain of epidemiol-
ogy including the Poisson random network, small-world network, and scale-
Network Visualization of Epidemiological Models 187

free network.
The visualization of network in Kendrick is done by the network builder
module. The example below represents a random network of a population
of 100 individuals. Each node of the network corresponds to an individual
of the population. The model is run as an individual-based simulation.
model := KEModel new population: (KEPopulation size: 100).

sirConcern := KEConcern new.


sirConcern addAttribute: #status value: #(S I R).
sirConcern addParameters: { #beta. #gamma. #lambda }.
sirConcern
addTransitionFrom: { #status->#S }
to: { #status->#I }
probability: 'lambda'.
sirConcern
addTransitionFrom: { #status->#I }
to: { #status->#R }
probability: 'gamma'.

network := KEContactNetwork nodes: 100 topology: { #random. #p->0.02 }.


spatialConcern := KEConcern new.
spatialConcern addParameter: #network value: network.
spatialConcern addAttribute: #node value: network allContacts.

model integrate: sirConcern.


model integrate: spatialConcern.

model
atParameter: #lambda
assignValue:
[ :aModel||node|
node := aModel currentCompartment at: #node.
((aModel atParameter: #network)
contactsOf: {aModel. #node->node. #status->#I})
* (aModel atParameter: #beta)/(aModel atParameter: #N)
].
model atParameter: #beta assignValue: 100.
model atParameter: #gamma assignValue: 0.1.

1 to: 99 do: [:i|


model atCompartment: {#status->#S. #node->i asString asSymbol} put: 1].
model atCompartment: { #status->#I. #node->#'100' } put: 1.

simulator := KESimulator new: #IBM from: 0.0 to: 50 step: 0.1.


simulator executeOn: model.
nb := KENetworkBuilder new
data: simulator allTimeSeries;
network: (model atParameter: #network);
188 Expressing Epidemiological Models

Figure 15.8: Visualizing a random network population with Kendrick.

status: #(#S #I #R);


colors: #(#green #red #blue);
viewDataAtTime: 10;
legend: 'random network, p = 0.02'.
nb open

We visualize the network of contacts after 10 days. The random network


is characterized by the probability of having an edge between two random
nodes (the argument p). The random network represents the contacts be-
tween individuals of the model and can be seen in Figure 15.8.
We may change the topology of the network to examine the effects of
other kind networks. For example, defining a scale-free network of 100
nodes with the first number of edges is m0:
network := KEContactNetwork nodes: 100 topology: { #scalefree. #m0->1 }.

The scale free network of the model after 10 days can be seen in Figure
15.9.
As can be seen on these figures, the random network shows a lack of clus-
tering because each two random nodes have the same probability to make
Network Visualization of Epidemiological Models 189

Figure 15.9: Visualizing a scale-free network population with Kendrick.

an edge. In the scale-free network, some individuals may have many more
contacts than others. The smallworld network is formed from a lattice grid
with some rewire nodes. This network is defined below:
network := KEContactNetwork nodes: 100 topology: { #smallworld. #K->2. #beta
->0 }.

Beta is the probability to rewire an edge in the network; K is the num-


ber of contacts on two sides of a node. By making K = 2 and beta = 0, we
can create a much simpler model like the ring network as shown in Figure
15.10. Such kinds of networks are often studied in the context of epidemiol-
ogy to show the social contacts between individuals (i.e, in the case of sexu-
ally transmitted diseases).
As mentioned above, a node of a contact network may represent a group
of individuals with some contact between one another. In such cases, in a
node, an individual is supposed to contact all others of the same node. The
probability of infection of a susceptible depends on the number of infectious
contacts within its node and in other linked nodes.
The example below represents a contact network of 10 nodes defined on
190 Expressing Epidemiological Models

Figure 15.10: Visualizing a small-world network population with Kendrick.

a population of 1,000 individuals. The visualization of the network can be


seen in Figure 15.11. We showed the infectious prevalence of each node (as
the size of nodes).

model := KEModel new population: (KEPopulation size: 1000).

sirConcern := KEConcern new.


sirConcern addAttribute: #status value: #(S I R).
sirConcern addParameters: { #beta. #gamma. #lambda }.
sirConcern
addTransitionFrom: { #status->#S }
to: { #status->#I }
probability: 'lambda'.
sirConcern
addTransitionFrom: { #status->#I }
to: { #status->#R }
probability: 'gamma'.
Network Visualization of Epidemiological Models 191

spatialConcern := KEConcern new.


network := KEContactNetwork nodes: 10 topology: { #random. #p->0.2 }.
spatialConcern addParameter: #network value: network.
spatialConcern addAttribute: #node value: network allContacts.

model integrate: sirConcern.


model integrate: spatialConcern.

model
atParameter: #lambda
assignValue:
[ :aModel||node|
node := aModel currentCompartment at: #node.
((aModel atParameter: #network)
contactsOf: {aModel. #node->node. #status->#I})
* (aModel atParameter: #beta)/(aModel atParameter: #N)
].
model atParameter: #beta assignValue: 1.
model atParameter: #gamma assignValue: 0.1.

2 to: 10 do: [:i|


model atCompartment: {#status->#S. #node->i asString asSymbol} put: 100].
model atCompartment: { #status->#I. #node->#'1' } put: 1.
model atCompartment: { #status->#S. #node->#'1' } put: 99.

simulator := KESimulator new: #IBM from: 0.0 to: 100 step: 0.1.
simulator executeOn: model.
nb := KENetworkBuilder new
data: simulator allTimeSeries;
network: (model atParameter: #network);
status: #(I); viewDataAtTime: 30.
nb open
192 Expressing Epidemiological Models

Figure 15.11: Visualizing a network contact model between groups of indi-


viduals with Kendrick.
Chapter 16

OpenStreetMap Integration

OpenStreetMap is a collaborative project to create maps (http://openstreetmap.


org) which offers a nice API to access and download map tiles.
Onil Goubier ([email protected]), Thierry Goubier
([email protected]), and Sergio Maass ([email protected])
have contributed to integration of OpenStreetMap in Roassal. These
contributions are under the MIT License.
The class RTOSM stands for OpenStreetMap and is a shape that down-
loads and renders tiles. Consider the following example:
v := RTView new.
map := RTOSM new.
e := map element.
v add: e.
v @ RTDraggableView.
RTEmptyViewContext setOnView: v.
v

Figure 16.1 is obtained by simply creating an element from the shape


RTOSM and adding it to a view. The map can be scrolled (thanks to v @
RTDraggableView). The Pharo inspector offers zooming in and out. When
visualizing OpenStreet map tiles, you may want to disable the automatic
camera positioning. This is achieved with RTEmptyViewContext setOnView: v.

16.1 Moving the camera to particular locations


The camera represents the location where the user sees the Roassal output,
and is located at the center of the window that renders the scene. The cam-
era may be located at any particular geographical location. Consider the
194 OpenStreetMap Integration

Figure 16.1: Simple example of using RTOSM.

following example that points to Paris:


v := RTView new.
map := RTOSM new.
e := map element.

v add: e.

paris := 48.8567 @ 2.3508.

v @ RTDraggableView.

v canvas camera translateTo: (map latLonToRoassal: paris).


v canvas camera noInitializationWhenOpen.
RTEmptyViewContext setOnView: v.
v

The variable paris contains a point latitude @ longitude. This coordinate is


easily obtained by googling for it or is available from Wikipedia.
Consider the following variation (Figure 16.3):
v := RTView new.
Moving the camera to particular locations 195

Figure 16.2: Pointing the camera to Paris.

map := RTOSM new.


e := map element.

v add: e.

v @ RTDraggableView.

movingCamera := [ :locationLatLong |
v canvas camera translateTo:
(map latLonToRoassal: locationLatLong).
v signalUpdate ].

"Adding a menu"
mb := RTMenuBuilder new.
mb view: v.
mb menu: 'Paris' callback: [ movingCamera value: 48.8567 @ 2.3508 ].
mb menu: 'London' callback: [ movingCamera value: 51.50722 @ -0.12750 ].
mb menu: 'NewYork' callback: [ movingCamera value: 40.7127 @ -74.0059 ].
mb build.
RTEmptyViewContext setOnView: v.
v
196 OpenStreetMap Integration

Figure 16.3: Adding a menu with locations.

The script above defines the block called movingCamera which takes as
argument a geographical location. The blocks move the camera accordingly
and signal a refresh. A menu is then defined to let the user click on each city
name to move the camera.

16.2 Decorating the map


Roassal elements may be added to a view, over a map. The method
RTOSM>>latLonToRoassal: is used to translate geographical coordinates into
Roassal space. Consider the following example that shows seismic activity
on Earth (Figure 16.4):
csv := (ZnEasy get: 'http://earthquake.usgs.gov/earthquakes/feed/v1.0/summary
/2.5_month.csv') contents.
tab := RTTabTable new
input: csv
usingDelimiter: $,.
tab removeFirstRow.
tab replaceEmptyValuesWith: '0' inColumns: #(2 3 5).
tab convertColumnsAsFloat: #(2 3 5).

v := RTView new.
map := RTOSM new.
e := map element.
v add: e.
tab values
do: [ :row |
e := (RTEllipse new
size: (2 raisedTo: row fifth) * 10;
Decorating the map 197

Figure 16.4: Seismic activity on Earth.

color: (Color red alpha: 0.3)) elementOn: row fifth.


e @ RTPopup @ RTHighlightable.
e translateTo: (map latLonToRoassal: row second @ row third).
v add: e ].

v canvas camera scale: 0.02; noInitializationWhenOpen.

v @ RTDraggableView.
RTEmptyViewContext setOnView: v.
v

The script begins by fetching data from the Earthquake Hazards Program
server. The fetched CSV file has to be slightly processed to replace double
commas („) entries by a zero (,0,) and to convert some columns into float
numbers. Column 2 of the file corresponds to the latitude of the event; Col-
umn 3 to the longitude; Column 5 to the seism intensity.
For each table row an ellipse is created with an exponential size. Each
produced element has a popup and is highlightable. The ellipse is then trans-
lated to its calculated position in the Roassal space.
A scale of 0.02 is used to give an overview of the map. Thanks to the noIni-
tializationWhenOpen setting the map keeps the 0.02 scale value when opened.
Without this setting, the value of 1 is used. The view is made draggable.
198 OpenStreetMap Integration

16.3 Getting country location


Some data are frequently given for a particular country. Visualizing data-
related country involves a translation from the country name to the geo-
graphical location. Consider the data offered by the United Nations High
Commissioner for Refugees (UNHCR, http://popstats.unhcr.org).
Here is an excerpt of a file obtained from the UNHCR server:
Year,Country/territory of residence,Total population
2013,Afghanistan,985191
2013,Albania,7747
2013,Algeria,95921
...

Visually representing the number of refugies on an OpenStreetMap re-


quires translating the word Afghanistan and all other country names into
points as latitude @ longitude. Since this is a frequent need, we provide fa-
cilities for this. The expression RTOSM downloadCountries uses restcountries.eu
to extract the list of countries and their position. The expression returns a
list of dictionaries. Since the result is faithfully structured as the HTTP re-
quest, extracting data is a bit cumbersome. Here is an example of getting the
geolocation of Afghanistan:
countries := RTOSM downloadCountries.
d := (countries detect: [ :dd | (dd at: 'name') = 'Afghanistan' ]) at: 'latlng'.

Consider the following script (Figure 16.5):


"Data extracted from http://popstats.unhcr.org/PSQ_POC.aspx"
countries := RTOSM downloadCountries.
tab := RTTabTable new input: RTOSM peopleForUNHCR usingDelimiter: $,.
4 timesRepeat: [ tab removeFirstRow ].
tab
convertColumn: 3
to: [ :value |
value = '*'
ifTrue: [ 0 ]
ifFalse: [ value asNumber ] ].
v := RTView new.
map := RTOSM new.
e := map element.
v add: e.

"We are interested in countries that have at least one refugee and we wort the
countries in reverse order according to the number of refugies they have. This
is helpful in the case of element overlaping"
interestingRows := (tab values reject: [ :row | row third < 1 ])
sorted: [ :a :b | a third > b third ].
Getting country location 199

Figure 16.5: Refugees monitored by the UNHCR.

interestingRows do: [ :row |


"Trying to get the country corresponding to the row"
theCountry := countries
detect: [ :each | '*' , (each at: 'name') , '*' match: row second ]
ifNone: [ nil ].
theCountry ifNotNil: [
| lat lon dict |
latLng := (theCountry at: 'latlng').
lat := latLng first.
lon := latLng second.
e := (RTEllipse new
size: (row third / Float pi) sqrt * 2;
color: (Color blue alpha: 0.3))
elementOn:
{(row second).
(row third)}.
e @ RTPopup.
e translateTo: (map latLonToRoassal: lat @ lon).
v add: e ] ].

v @ RTDraggableView @ RTZoomableView.
RTEmptyViewContext setOnView: v.
v canvas camera focusOnCenterScaled.
v

The method peopleForUNHCR contains the data fetched from the UNHCR
server.
Chapter 17

Network Latency

Nowadays, people spend a lot of time using electronic gadgets such as com-
puters, smartphones or tablets. As time goes by, all of these devices seem
useless without Internet connectivity. Users always want to improve their
experience while using Internet services. They acquire Internet connections
with the highest bandwidth possible; they buy the phone which promises
the fastest connectivity by using 4G; and they sign up for residential fiber-
optic Internet. All of these options ar eexpected to offer the best experience.
However, there is a hidden element that can affect user experience that most
users do not consider, which is network latency.
This chapter is written by Leonel Merino ([email protected]), PhD stu-
dent from the University of Bern.

17.1 Prelude

The latency of a network is the sum of the latency added by every node that
takes part in a network communication. Each network node has an inherent
latency, specified by its manufacturer. The latency of a network node —such
as a router, a switch, a hub— is the time elapsed from the moment when it
receives a packet and when it sends a response. The path between two com-
puters in a network is dynamic and normally implies several hops through
intermediate nodes. Therefore, the latency of a connection will depend on
the path that it takes. The range of network latency in Internet are normally
in the order of milliseconds. Since the latency is a base delay in each con-
nection, protocols that open many connections are more affected. In conse-
quence, services such as music, video and online games which typically split
data into a bunch of small packets are more sensitive to high latency.
202 Network Latency

Motivation
When a node of the network fails, the communication paths that pass by the
node increase their latency (the impact depends on the nature of the failure).
From the point of view of the company which offers the Internet service,
there is a need for monitoring how is latency levels among the nodes of the
network. In case of a failure, the nodes may react and take corrective actions.
Visual tools are extensively used for monitoring services. They allow opera-
tors to spot failures at a glance, or in other cases to be aware of how close the
latency is to the threshold the user experience is compromised.

Case study
In our case study we analyzed a proxy service that operates 145 nodes dis-
tributed in different cities around the world. By using Roassal we developed
a tool for visualizing the status of the service. We called the tool Network
Latency Visual Analyzer (NLVA). The tool envisions two kinds of users: a
company that manages a network and clients of that company. The tool of-
fers different views of the dataset which helps the company to be aware of
failures (to spot where possible failures occurred and what potential users
can be affected); to take actions for improving the service by highlighting
nodes that are geographically close but that have a high latency —in com-
parison with the rest of them— allowing the company to possibly change
the service provider between those nodes; and to spot where the best end-
points are for each node that offers the lowest latency, which can be of help
to end users —e.g., a gamer who wants to know to which server should he
connect to minimize the lag.

17.2 Network Latency Visual Analysis in a Nut-


shell
When developing NLVA we did not want to start from scratch. We thought
that the MapBuilder example, which comes with Roassal offers many of the
features that we wanted to have in this tool. However, we decided not to
directly modify it or to replicate its code. We reused it by extending some
parts of it to fit our purpose. The package NetworkLatency contains the NLVA
implementation, made of six classes: NLCityBuilder, NLMapBuilder, NLPopup,
NLRoute, NLRouteBuilder, and NLVisualise.
All of these classes except NLVisualise extend a Roassal counterpart.
We follow this naming convention RTClassName —> NLClassName. These
classes add or modify features of the original MapBuilder implementation.
The remaining class NLVisualise is the entry point to run the visualization. It
Network Latency Visual Analysis in a Nutshell 203

is the main class that holds methods to retrieve the dataset, to trigger ping
commands in the local machine, and to build the visualization.

Resources
NLVA comes with a dataset that contains a snapshot of the latency between
servers of a network. It also includes the location (lat/long) of the cities
highlighted in the visualization. In addition, it holds IP addresses that are
physically located in each city (used when testing user local connectivity).
These data were collected from the following sites:

Resource URL
Network Latency dataset https://wonderproxy.com
IP Location http://www.iplocation.net
Geolocation http://www.latlong.net

Visual Queries
The following queries are examples showing that a visualization of the
dataset provides a quick notion of the answer that can be followed by a de-
tailed analysis of the latency measures. At the end, it can help to exclude
candidates from the set of possible answers, narrowing the space of solu-
tions. As an exercise the reader can surely find more queries that can be
supported by the different views included in the tool.

1. Where should a company using this network store content for deliver-
ing to users in Paris? (Hint: Best)

2. Do you notice some possible failures in the network that may impact
the latency? (Hint: Worst)

3. Are cities with better connectivity distributed equally in both hemi-


spheres? (Hint: Average)

4. What are the cities where the company should invest to improve over-
all network latency? (Hint: CloseHigh)

5. If you want to provide a service in both the US and Europe (using this
network), where would be the best two cities to locate a host? (Hint:
FarLow)

6. If I am in the Paris, to which continents should I prioritize connections?.


How would my service be affected if a move it from Paris to New York?
(Hint: Left-click on a city)
204 Network Latency

Figure 17.1: Use the plugin to load NLVA.

7. How is the network latency in general from my city? (Hint: Right-click


on your city if listed or in another region of a country)

8. Would I improve my connectivity using the service from which the


dataset comes (over my actual provider)? (Hint: Comparing the results
of left and right click if your city is included in the visualization)

17.3 Loading NLVA


There are two ways to load NLVA. It can be loaded using using the Network-
Latency plugin, accessible from the World menu (Figure 17.1).
Programmatically, you can load it using a Gofer expression, executed in a
Playground. Simply copy & paste the following expression in a Playground,
and evaluate it:
Visualizing latency in a proxy service 205

Gofer new smalltalkhubUser: 'merino'


project: 'NetworkLatency';
package: 'ConfigurationOfNetworkLatency';
load.
(Smalltalk at: #ConfigurationOfNetworkLatency) loadDefault

Afterwards, you launch the tool by evaluating:


NLVisualise new open

17.4 Visualizing latency in a proxy service


NLVA shows a visualization of a map of the world highlighting 145 cities
spread over 70 countries (purple circle). NLVA comes with several views
of the data shown in a menu at the top of the window that helps to move
from different views such as ’Best’, ’Worst’, ’MST’ (Minimal Spanning Tree),
’Close High’ and ’Far Low’. The default view is ’Best’, which shows where
the city is with which it has the best connectivity (in terms of latency). The
color of the edge between the cities represents the latency of the connection.
Green edges are the best connections; orange edges indicate an average qual-
ity, red ones are worse, and dark red edges indicate the worst latencies (the
ranges are split in [0,100ms[, [100ms, 300ms[, [300ms, 1000ms[ and greater
than 1000ms).
There is also another visualization which is not triggered from the menu.
By left-clicking in any city (purple circles) the user can see the latency from
that city to the other cities (included in the dataset).
These visualizations show data which comes from a private company
(see Resources) that offers a Internet Proxy service. The company shows
the status of their service in its website from which the data was manually
collected. The data can be inaccurate (the snapshot was taken at the end
of 2014), so its only purpose is to be a proof-of-concept for showing how
network latency data can be analyzed in a visual manner.

17.5 Visualizing your own connectivity


NLVA also includes a feature to allow users to test their own connectivity. To
run it you have to right-click on the map approximately where your location
is (the city can be already included in the map or not). A warning will popup
asking you if you really want to run the test. It takes just a couple of seconds.
It will show edges from your location to the rest of the cities included in the
visualization (specifying their latency). A relevant analysis is to compare
206 Network Latency

Figure 17.2: Worst latency for each city. Note that Valencia and Nairobi con-
centrate the most of the worst endpoints for connections.

Figure 17.3: Average latency for each city.


Visualizing your own connectivity 207

Figure 17.4: MST shows what probably are physical wires connecting cities.

Figure 17.5: FarLow. Far cities that show low latency. Most East Coast cities
of the US are the best endpoints for European connections. A couple of un-
expected results Joao Pessoa-Orlando and Portland-Anchorage.
208 Network Latency

Figure 17.6: Zooming-in on the Portland-Anchorage latency.

Figure 17.7: Latencies from Brisbane.


Visualizing your own connectivity 209

Figure 17.8: Latencies from ”Bern”, my actual location.

those results with the integrated data that comes with the tool —caveat: it
can only be done for the cities that come with the visualization (left and
right-click).
Chapter 18

Analyzing Software using


Moose

Moose is a platform for data and software analysis. Moose is the result of a
collaborative effort, initiated at the University of Bern, which now comprises
several companies and research groups spread all over the world. The web-
site of Moose is http://moosetechnology.org.
Visualization plays an important role in assessing the quality of a soft-
ware. The source code defining a software may be very large and is charac-
terized using a wide range of metrics and structural properties. Identifying
anomalies, and more importantly, proposing actions to improve the quality
is difficult. It is known that visualization greatly alleviates software mainte-
nance.
Moose is often used to craft specific software engineering tools that guide
engineers in making proper decisions about the course of a software develop-
ment. Humane Assessment is a method designed by Tudor Gîba and largely
described on the website http://www.humane-assessment.com.
What Moose is good for: Moose is made to carry out analysis on the source
code structure. A whole range of analyses may be conducted by analysis
source code. Moose is also frequently used to analyze configuration files,
process descriptions, or mailing lists.
What Moose is not good for: Moose cannot directly be used to analyze the
dynamic execution of an application. Dynamic analysis consists in recording
relevant information during a program execution. Such information may
then be used to spot particular behavior.
212 Analyzing Software using Moose

Figure 18.1: Opening the Moose Panel.

18.1 Moose in a nutshell


The core of Moose is composed of:

• Panel: An extensible tool to navigate, visualize, and formulate queries.

• Parsers: Two parsers are shipped with Moose. MSE is a file-format


describe source code models. MSE is typically produced by externally
provided parsers.

• The Famix metamodel: An abstract representation of source code. Famix


is generic and it may describe applications written in Java, C, C#, and
Pharo.

• Chef : A domain-specific language to query structural dependencies.


Analysis the dependencies between software elements is particularly
difficult. Chef allows one to precisely formulate queries

18.2 Loading a software application


Creating a model in Moose is probably the very first step to take. Such a
model will contains all the software elements on which your analyse will be
carried out. The standard way to create a model is to load an application.
To do so, the Moose panel has to be open. The panel is accessible from the
World menu (Figure 18.1).
Loading a software application 213

Moose has two built-in parsers: one for Pharo, and another for MSE. The
first parser is accessible by clicking on the ST icon, top-right of the Moose
Panel. Clicking on it will open a package selector. You simply have to select
the Pharo packages you wish to include in your model. The second parser
takes as input a MSE file, accessible by clicking on the MSE button.
Consider the following Java code:
package agilevisualization.example;

public class HelloWorld {


public static void main(String[] argv) {
System.out.println("Hello World");
}
}

The corresponding MSE file of that code is 48 lines long. The first few
lines are:
(
(FAMIX.Inheritance (id: 3)
(subclass (ref: 10))
(superclass (ref: 4)))
(FAMIX.Class (id: 4)
(name 'Object')
(container (ref: 7))
(isStub true))
(FAMIX.FileAnchor (id: 5)
(element (ref: 1))
(endLine 6)
(fileName './agilevisualization/example/HelloWorld.java')
(startLine 4))
(FAMIX.FileAnchor (id: 6)
(element (ref: 10))
(endLine 7)
(fileName './agilevisualization/example/HelloWorld.java')
(startLine 3))
...
)

Producing a MSE file from Java source code is done using VerveineJ. Un-
fortunately, at the time this chapter was written, VerveineJ has a proprietary
license. Contact the book author or the Moose mailing list for more informa-
tion.
Along this chapter, we will use a running example, obtained from the
Java application checkstyle. Checkstyle is a development tool to help pro-
grammers write Java code that adheres to a coding standard. The source
code of Checkstyle is available from its website: http://checkstyle.sourceforge.
214 Analyzing Software using Moose

Figure 18.2: Loading checkstyle.

net. We have produced a MSE file, available on http://bit.ly/checkstyle.

18.3 Browsing the source code


After having loaded the checkstyle MSE file, a new model is listed in the
Moose (Figure 18.2).
Selecting the checkstyle model in the panel lists the contained entities.
The panel indicates a few interesting facts about Checkstyle, for example, it
is composed of

• 4,824 methods (item All methods)

• 1,011 classes (item All model classes)

• 36 packages (item All model namespaces)

The item All classes lists all the classes defined in the application and all
Browsing the source code 215

Figure 18.3: Querying in the panel.

the classes used by checkstyle that are not defined in it. Some examples of
such classes are Object, String, Integer, which are defined in the Java runtime.
Select the All model classes item to list all the classes defined in the appli-
cations. Below the list of classes, a text input accepts filtering queries. Enter
each numberOfMethods > 30. A third pane appears and lists the classes de-
fined in checkstyle that have more than 30 methods (Figure 18.3).
Since the items displayed in the panel All model classes are instances of
the class FAMIXClass, the each variable refers to an FAMIXClass. Browsing
this class using the Pharo code browser will give you the list of methods that
are understandable by the each variable.
Queries involving the class name are also often formulated. For example,
the query
'*Log*' match: each name

designates classes defined in Checkstyle that have the word Log in their
name. The string being an expression regular, matched using match:.
Another relevant query to type in is: each isStub. You need to select the
item All classes instead. This simple query filters out all the classes that be-
longs to the checkstyle application, to leave the classes are used by Check-
style. Said in other words, all the externally provided classes used by Check-
style are remains. This is handy to assess external dependencies. In the
Moose jargon, a stub class is a class that is used by the analyzed application
but not defined in it. Checkstyle contains 398 stub classes, corresponding
to classes defined in other libraries, not part of the .MSE file. For example,
216 Analyzing Software using Moose

Checkstyle depends on some libraries provided by the Apache foundation


or Google. These libraries are not part of the .MSE file.
Query may be relatively complex. For example, consider:
each numberOfMethodsOverriden > (each numberOfMethodsInherited / 3)

This query returns the classes that override more than 1/3 of the classes
that it inherits. Such classes are rather suspicious since it may be worth con-
sidering whether they belong to the adequate class hierarchy.

18.4 Visualizing the code distribution


Checkstyle contains more than one thousand classes, distributed over 36
packages. Understanding the code distribution over these packages may in-
volve a dedicated visualization. After having selected the checkstyle model,
select the packages by pressing All model namespaces. The new pane located
on the right-hand side lists these packages. Oddly enough, the packages
<Default Package> and blah are listed.
Pressing the [|] tab on the right pane allows one to enter a script. The self
variable is then bound to the collection of packages. Enter the script (Figure
18.4):
b := RTMondrian new.
b nodes: (self reverseSortedAs: #numberOfClasses).
b layout flow.
b normalizer
normalizeWidth: #numberOfClasses max: 100;
normalizeHeight: #numberOfLinesOfCode max: 100.
b

This simple visualization shows that not all the packages con-
tain the same amount of code. The first package, the largest, is
com.puppycrawl.tools.checkstyle. Clicking on the package opens up a new pane.
The second tab gives the list of properties for that package. In indicates that
it defines 142 classes, spread over 5,617 lines of code.
Clicking on the entry Types lists all the classes defined in the package
checkstyle. The following script reveals a number of facts regarding these
142 classes (Figure 18.5):
b := RTMondrian new.
b shape rectangle
width: #numberOfAttributes;
height: #numberOfMethods.
b nodes: (self reverseSortedAs: #numberOfMethods).
b edges connectFrom: #superclass.
Visualizing the code distribution 217

Figure 18.4: Querying in the panel.

Figure 18.5: Classes in the largest package.

b normalizer
normalizeColor: #numberOfLinesOfCode.
b layout flow.
b

Most of the classes are small, in terms of lines of code, number of defined
methods and attributes. Moreover, only two of these classes are subclasses.
218 Analyzing Software using Moose

Figure 18.6: Visualizing cross-cutting concerns.

18.5 Visualizing cross-cutting concerns

Cross-cutting features are features that crosscut software structural entities,


such as classes or packages. Due to their nature, cross-cutting are not ex-
plicitely modularized, and may therefore lead to some code anomalies. Con-
sider the following script executed on the list of model classes (Figure 18.6):

b := RTMondrian new.
b shape box
if: [ :c | c methods anySatisfy: [ :m | '*error*' match: m name ] ]
fillColor: Color blue;
if: [ :c | c methods anySatisfy: [ :m | 'visit*' match: m name ] ]
fillColor: Color red.
b nodes: self.
b edges connectFrom: #superclass.
b layout tree.
b normalizer
normalizeHeight: #numberOfMethods max: 100;
normalizeWidth: #numberOfAttributes max: 100.

b build.
b view @ RTDoubleScrollBar.
b view

The script colors in blue classes that have at least one method with the
word error in it and in red classes that are visiting some entities. The hierar-
chy of red classes indicates a specialization of visitors. Notice that that errors
are rarely handled.
Dependencies using Moose Chef 219

Figure 18.7: Dependencies between classes.

18.6 Dependencies using Moose Chef

Moose chef is an API allowing one to query dependencies between packages,


classes, and methods. Consider the following script, running on the list of
model classes (Figure 18.7):

b := RTMondrian new.
b nodes: self.
b edges connectFrom: #superclass.
b layout cluster.
b shape
bezierLineFollowing: #superclass;
color: Color blue.
b edges
notUseInLayout;
connectToAll: [ :cls | cls queryAllOutgoingInvocations atTypeScope ].
b

Sending the message queryAllOutgoingInvocations to a Famix class returns


a list of Famix invocations. An invocation is an instance of the Famix class
FAMIXInvocation. Invocations represent a low level abstract that often need to
be transformed. Such transformation is called setting a scope by Moose Chef.
The invocations may be scoped to the classes by sending atTypeScope. For
a given Famix class cls, the expression cls queryAllOutgoingInvocations atType-
Scope returns the list of Famix classes that cls depends on. A Chef query may
be scoped using atNamespaceScope, atPackageScope, atTypeScope, atMethod-
Scope.
220 Analyzing Software using Moose

Figure 18.8: Inspecting a class group.

Figure 18.9: The pencil icon to open a system browser.

18.7 Reusing visualization


So far, we have defined some visualizations within the tab [|]. This allowed
us to play with and tune the visualization as we need. Once satisfied with
a visualization, you may want to install it permanently, quickly launch the
visualization for a different set of classes.
One easy way to install a visualization, is to create a method on the class
describing the group you are interested in. For example, if you wish to make
a visualization for a group of classes, then FAMIXClassGroup is probably the
class you wish to install the visualization in.
First, you need to inspect the class behind the group you are interested
in, as shown in Figure 18.8. This opens a new window titled Inspector on a
FAMIXClassGroup. Click on the pencil icon (Figure 18.9).
Define the method on the class FAMIXClassGroup:
FAMIXClassGroup>>viewDependencies
<menuItem: 'Dependencies' category: 'Visualize'>
|b|
b := RTMondrian new.
b nodes: self.
b edges connectFrom: #superclass.
b layout cluster.
b shape
bezierLineFollowing: #superclass;
color: Color blue.
More on that topic 221

Figure 18.10: A new entry has been added in the sub menu Visualize.

b edges
notUseInLayout;
connectToAll: [ :cls | cls queryAllOutgoingInvocations atTypeScope ].
b inspect

After defining this method, right clicking on the All model classes item in
the Moose Panel gives a new menu entry called Dependencies (Figure 18.10):

18.8 More on that topic


Moose is a large project that contains many exciting facets. This chapter only
superficially highlights some of them. Another good source of documenta-
tion is the Moose Book (http://www.themoosebook.org/book), project lead by Dr.
Tudor Gîrba http://www.tudorgirba.com.

You might also like