Skip to content

Latest commit

 

History

History
333 lines (260 loc) · 9.2 KB

File metadata and controls

333 lines (260 loc) · 9.2 KB

Tutorial 01 - Adding Simple Nodes, Edges, and Layout

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: alt text

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.

alt text

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.

alt text

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.

alt text

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 gray
LesMiserableVisualization>>>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 red
LesMiserableVisualization>>>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: 50
LesMiserableVisualization>>>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: characters

The visualization can be invoked using:

LesMiserableVisualization>>>open
	"Open the Roassal canvas"
	canvas open

By 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;
	open

to produce: alt text

We can refine the script to have:

LesMiserableVisualization new
	adjustColor;
	adjustFontShape;
	addLines;
	doLayout;
	open

to produce: alt text

A variant of the visualization may be easily built:

LesMiserableVisualization new
	adjustColorShapeFrom: Color white to: Color black;
	adjustFontShapeFrom: 5 to: 100;
	addLines;
	doLayout;
	open

to produce: alt text

A new layout may be adopted:

LesMiserableVisualization new
	adjustColorShapeFrom: Color white to: Color black;
	adjustFontShapeFrom: 5 to: 100;
	doLayout: (RSFlowLayout new);
	open

to produce: alt text