This is a tutorial for Roassal3. In order to go through it, you need to have Pharo installed, and Roassal3 installed.
This tutorial involves some data manipulation. We will use the dataset of Les Miserables, which can be loaded in Pharo using:
Metacello new
baseline: 'LesMiserables';
repository: 'github://bergel/LesMiserables';
load.The dataset essentially contains two kinds of interesting data: characters of the historical novel and coappearances of the characters. We will visualize this data.
After loading Les Miserables and Roassal3, you can execute the following script:
"Extract data from the French historical novel Les Miserables"
m := LMModel new create.
"Each character is represented as a small ellipse"
characters := m characters collect: [ :c | RSEllipse new size: 5 ].
"c is a canvas. This is where we can draw"
c := RSCanvas new.
c addAll: characters.
"All the characters are displayed using a grid layout"
RSGridLayout on: characters.
"The canvas can be zoomed in / out using keys I and O"
"It can also be navigated using scrollbars"
c @ RSCanvasController.
This produces the following output:

We can normalize the size of each character based on the number of times she or he coappears:
"Extract data from the French historical novel Les Miserables"
m := LMModel new create.
"Each character is represented as a small ellipse"
characters := m characters collect: [ :c | RSEllipse new size: 5; model: c ] as: RSGroup.
"c is a canvas. This is where we can draw"
c := RSCanvas new.
c addAll: characters.
"We normalize the size of the characters using their coappearances"
RSNormalizer size
shapes: characters;
normalize: #numberOfCoappearances.
"All the characters are displayed using a grid layout"
RSGridLayout on: characters.
"Make each element have a popup text"
characters @ RSPopup.
"The canvas can be zoomed in / out using keys I and O"
"It can also be navigated using scrollbars"
c @ RSCanvasController.We can add edges to indicate coappearances:
"Extract data from the French historical novel Les Miserables"
m := LMModel new create.
"Each character is represented as a small ellipse"
characters := m characters collect: [ :c | RSEllipse new size: 5; model: c ] as: RSGroup.
"c is a canvas. This is where we can draw"
c := RSCanvas new.
c addAll: characters.
"We normalize the size of the characters using their coappearances"
RSNormalizer size
shapes: characters;
normalize: #numberOfCoappearances.
"The same normalization using colors"
RSNormalizer color
shapes: characters;
from: Color gray translucent;
to: Color red translucent;
normalize: #numberOfCoappearances.
eb := RSEdgeBuilder arrowedLine.
eb canvas: c.
eb color: Color gray.
eb moveBehind.
eb shapes: characters.
eb connectToAll: #characters.
"All the characters are displayed using a force based layout"
RSForceBasedLayout new charge: -900; on: characters.
"Make each element have a popup text and allow it to be dragged"
characters @ RSPopup @ RSDraggable.
"The canvas can be zoomed in / out using keys I and O"
"It can also be navigated using scrollbars"
c @ RSCanvasController.Circles can be replaced with labels:
"Extract data from the French historical novel Les Miserables"
m := LMModel new create.
"Each character is represented as a label"
characters := m characters collect: [ :c | RSLabel
new size: 5;
text: c;
model: c ] as: RSGroup.
"c is a canvas. This is where we can draw"
c := RSCanvas new.
c addAll: characters.
"We normalize the font size of the characters using their coappearances"
RSNormalizer fontSize
shapes: characters;
from: 5;
to: 50;
normalize: #numberOfCoappearances.
"The same normalization using colors"
RSNormalizer color
shapes: characters;
from: Color gray translucent;
to: Color red translucent;
normalize: #numberOfCoappearances.
eb := RSEdgeBuilder arrowedLine.
eb canvas: c.
eb color: Color gray translucent.
eb moveBehind.
eb shapes: characters.
eb connectToAll: #characters.
"The characters are displayed as a force-directed graph"
RSForceBasedLayout new charge: -1500; on: characters.
"Make each element have a popup text and allow it to be dragged"
characters @ RSPopup @ RSDraggable.
"The canvas can be zoomed in / out using keys I and O"
"It can also be navigated using scrollbars"
c @ RSCanvasController.So far we interactively built a script to visualize a small dataset. Although a good start, it's not the best approach when extensibility and customizability is desired. A script, such as the one we provided, has the benefit of conciseness and is self-contained, but it is relatively complex and requires greater overall understanding in order to be adapted and extended. To achieve the latter, we can wrap it with a dedicated class and break up the script into individual methods. As such, we can create the class LesMiserableVisualization:
Object subclass: #LesMiserableVisualization
instanceVariableNames: 'model canvas characters'
classVariableNames: ''
package: 'LesMiserableVisualization'The temporary variables we had in our script are converted to instance variables. Initialization can be done in the initialize method:
LesMiserableVisualization>>>initialize
super initialize.
canvas := RSCanvas new.
"The canvas can be zoomed in / out using keys I and O"
"It can also be navigated using scrollbars"
canvas @ RSCanvasController.
self createModel.The creation of the model is achieved using:
LesMiserableVisualization>>>createModel
"Create the model and create visual characters"
model := LMModel new create.
self createCharacters.The characters may be built with:
LesMiserableVisualization>>>createCharacters
"Create a visual element for each character of Les Miserables"
"Each character is represented as a label"
characters := model characters
collect: [ :c |
RSLabel new size: 5; text: c; model: c ]
as: RSGroup.
"Add the characters to the canvas"
canvas addAll: characters.
"Make each element have a popup text and is draggable"
characters @ RSPopup @ RSDraggable.We can now focus on the necessary methods to actually build the visualization. Consider the following methods to build coappearance relationship:
LesMiserableVisualization>>>addLines
self addLinesColored: Color grayLesMiserableVisualization>>>addLinesColored: aColor
"Add coappearances using colored lines "
| eb |
eb := RSEdgeBuilder line.
eb canvas: canvas.
eb color: aColor translucent.
eb moveBehind.
eb shapes: characters.
eb connectToAll: #characters.Similarly, we can provide the necessary methods to adjust the colors of the characters:
LesMiserableVisualization>>>adjustColor
self adjustColorShapeFrom: Color gray to: Color redLesMiserableVisualization>>>adjustColorShapeFrom: color1 to: color2
"Normalizing the characters"
RSNormalizer color
shapes: characters;
from: color1 translucent;
to: color2 translucent;
normalize: #numberOfCoappearances.The font shapes may be adjusted in a similar fashion:
LesMiserableVisualization>>>adjustFontShape
self adjustFontShapeFrom: 5 to: 50LesMiserableVisualization>>>adjustFontShapeFrom: smallFontSize to: bigFontSize
"We normalize the font size of the characters using their coappearances"
RSNormalizer fontSize
shapes: characters;
from: smallFontSize;
to: bigFontSize;
normalize: #numberOfCoappearances.Layout can be achieved using the following two methods:
LesMiserableVisualization>>>doLayout
self doLayout: (RSForceBasedLayout new charge: -1500)LesMiserableVisualization>>>doLayout: aLayout
"Perform the layout on the characters"
aLayout on: charactersThe visualization can be invoked using:
LesMiserableVisualization>>>open
"Open the Roassal canvas"
canvas openBy breaking up the original scripts into a set of parametrized methods, we turned our script into a small and customizabler application. For example, we can perform:
LesMiserableVisualization new
doLayout;
openWe can refine the script to have:
LesMiserableVisualization new
adjustColor;
adjustFontShape;
addLines;
doLayout;
openA variant of the visualization may be easily built:
LesMiserableVisualization new
adjustColorShapeFrom: Color white to: Color black;
adjustFontShapeFrom: 5 to: 100;
addLines;
doLayout;
openA new layout may be adopted:
LesMiserableVisualization new
adjustColorShapeFrom: Color white to: Color black;
adjustFontShapeFrom: 5 to: 100;
doLayout: (RSFlowLayout new);
open





