© Alexandre Bergel 2022
A. BergelAgile Visualization with Pharohttps://doi.org/10.1007/978-1-4842-7161-2_8

8. Line Builder

Alexandre Bergel1  
(1)
Santiago, Chile
 

This chapter describes an expressive way to build lines in Roassal. I’ll start by illustrating the difficulties in defining lines. Subsequently, I’ll demonstrate how Line Builder is used to address these difficulties.

All the code provided in this chapter is available at https://github.com/bergel/AgileVisualizationAPressCode/blob/main/02-04-LineBuilder.txt.

Difficulties with Build Lines

A visualization typically represents a particular set of elements and logical structure is best expressed using lines. Manually maintaining the association between the represented elements and the visual representation can be very cumbersome. Consider the following example, borrowed from the field of software visualization. In Pharo, a software component is typically expressed in term of classes, which are possibly linked to each other using inheritance. Visually representing the inheritance of a class hierarchy is a powerful mechanism that the UML class diagram is based on. Consider the following code snippet:
classes := Collection withAllSubclasses.
c := RSCanvas new.
"Build nodes"
boxes := RSCircle models: classes.
c addAll: boxes.
RSFlowLayout on: c shapes.
"Connect nodes"
classes do: [ :cls |
    fromBox := boxes shapeFromModel: cls superclass.
    toBox := boxes shapeFromModel: cls.
    fromBox notNil ifTrue: [
        line := RSLine new.
        line from: fromBox.
        line to: toBox.
        c add: line ].
     ].
RSTreeLayout on: c nodes.
c @ RSCanvasController.
c open
../images/489192_1_En_8_Chapter/489192_1_En_8_Fig1_HTML.jpg
Figure 8-1

Joining each class to its superclass

The way that nodes are connected is rather complex. First, an iteration is explicitly performed to retrieve the two extremities using shapeFromModel:. However, the box representing a superclass might not exist, as is the case of the Collection class, whose superclass is Object, and is not contained in the classes variable.

Using a Line Builder

The RSLineBuilder class greatly simplifies the creation of lines. Consider this improved version of the code given previously:
classes := Collection withAllSubclasses.
c := RSCanvas new.
boxes := RSCircle models: classes.
c addAll: boxes.
RSFlowLayout on: c shapes.
lb := RSLineBuilder line.
lb shapes: boxes.
lb connectFrom: #superclass.
RSTreeLayout on: c nodes.
c @ RSCanvasController.
c open
The result of this improved version is identical to the original. The benefits of using the Line Builder are obvious. There is:
  • No need to “manually” iterate over a collection to retrieve the shape from a given object

  • No need to check if a shape is missing

Furthermore, Line Builder offers numerous methods to easily create lines. For example:
classes := Collection withAllSubclasses.
c := RSCanvas new.
boxes := RSCircle models: classes.
c addAll: boxes.
RSFlowLayout on: c shapes.
lb := RSLineBuilder orthoVertical.
lb withVerticalAttachPoint.
lb capRound.
lb shapes: boxes.
lb connectFrom: #superclass.
RSTreeLayout on: c nodes.
c @ RSCanvasController.
c open
../images/489192_1_En_8_Chapter/489192_1_En_8_Fig2_HTML.jpg
Figure 8-2

Using a Line Builder

Line Builder is used to produce ortho-vertical lines instead of straight lines. By default, a line connect shapes from their centers. This default behavior can be overridden using withVerticalAttachPoint in this example. Corners of the ortho-vertical lines are rounded using capRound (this effect is apparent when zooming in).

The builder looks for extremities in the shapes, which are provided using shapes:. Each line produced from a shape has the “from” extremity referring to the superclass.

Using Associations

In Pharo, the method called -> creates an association, an instance to the Association class. An association links a key to a value. Due to its syntactic conciseness, associations are handy ways to specify connections between elements.

Line Builder can use associations to build lines. Consider this script (see Figure 8-3):
b := RSCanvas  new.
b addAll: (RSCircle models: (1 to: 4)).
b shapes @ RSDraggable @ RSPopup.
RSLineBuilder line
    color: Color red translucent;
    canvas: b;
    withBorderAttachPoint;
    useAssociations: { 1 -> 2 . 2 -> 3 . 4 -> 1 . 3 -> 4 }.
RSGridLayout new gapSize: 30; on: b nodes.
b @ RSCanvasController.
b open
../images/489192_1_En_8_Chapter/489192_1_En_8_Fig3_HTML.jpg
Figure 8-3

Graph building with Line Builder

Graph Visualization

Line Builder significantly reduces the effort needed to specify connections, as commonly used to represent graphs. Consider the following code snippet (see Figure 8-4):
numberOfNodes := 25.
numberOfLines := 90.
r := Random seed: 42.
graph := Dictionary new.
1 to: numberOfNodes do: [ :aNode |
    graph at: aNode put: Set new ].
numberOfLines timesRepeat: [
    fromNode := r nextInteger: numberOfNodes.
    toNode := r nextInteger: numberOfNodes.
    (graph at: fromNode) add: toNode ].
canvas := RSCanvas new.
nodes := RSLabel models: (1 to: numberOfNodes).
nodes color: #red.
nodes @ RSDraggable @ RSPopup.
canvas addAll: nodes.
lb := RSLineBuilder line.
lb canvas: canvas.
lb withBorderAttachPoint.
lb makeBidirectional.
lb moveBehind.
lb objects: (1 to: numberOfNodes).
lb connectToAll: [ :aNumber | graph at: aNumber ].
RSForceBasedLayout new charge: -300; on: nodes.
canvas @ RSCanvasController.
canvas open
../images/489192_1_En_8_Chapter/489192_1_En_8_Fig4_HTML.jpg
Figure 8-4

Graph visualization

The beginning of the script generates a graph made of numberOfNodes nodes and numberOfLines lines. The graph variable is a dictionary that contains connections. The Line Builder object, lb, is in charge of drawing edges between nodes. The builder moves the lines behind the node using moveBehind. This is useful since having lines in front may hide the nodes. A border attach point is set, and the lines are anchored on the border of each label, the one closest to the other extremity. Furthermore, lines are bidirectional to avoid redundant lines (e.g., only one line from 1− > 2 and 2− > 1 is constructed). This is useful since the graph is randomly generated.

This example illustrates the usefulness of Line Builder. The same example without Line Builder involves a significant amount of code.

What Have You Learned in This Chapter?

Line Builder is frequently used due to its expressiveness. Line Builder is an important asset of Roassal, and I recommend carefully studying it. In particular, Line Builder aims at:
  • Simplifying the construction of many lines.

  • Providing utility methods to control the placement and generation of lines.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.97.126