Skip to content

02 Canvas

Milton Mamani Torres edited this page Apr 29, 2020 · 5 revisions

Canvas

RSCanvas is the main class for roassal visualizations, it is a container of shapes and animations, and describes how this elements interact with the Pharo ecosystem, you can inspect it or open it in a system window, or create a morph and put it into a new window.

canvas := RSCanvas new.
canvas color: Color white.
window := canvas open.
window setLabel: 'Roassal canvas'.

the previous example uses open, this method open a new SystemWindow instance, and then we can change the window title.

Shapes in RSCanvas are separated in different groups, shapes, fixedShapes, nodes and edges.

Camera

RSCanvas has a reference to an instance of RSCamera, this object modifies the position and the scale of the group shapes, it has some methods to adjust the camera position and scale to focus to the elements.

canvas := RSCanvas new.
0 to: 10 do: [ :i | 
	canvas add: (RSBox new
		position: (i@i)* 20;
		color: (i even 
			ifTrue: [Color red ]
			ifFalse: [ Color blue ] );
		size: 20;
		yourself)  ].
canvas

In the previous example we put 10 red-blue boxes. The origin for these shapes is the center of the canvas. The first red box is the center of the visualization. But the visualization is not focus on the shapes, there are white areas that takes a lot of space. with the camera we can change the position to focus all the shapes.

...
canvas zoomToFit.

With zoomToFit canvas changes the position and scale of the camera without changing the position and scale of each shape.

Also just for fun we can change the viewing area of the camera to focus only on the first 3 first boxes.

canvas := RSCanvas new.
0 to: 10 do: [ :i | 
	canvas add: (RSBox new
		position: (i@i)* 20;
		color: (i even 
			ifTrue: [Color red ]
			ifFalse: [ Color blue ] );
		size: 20;
		yourself)  ].
group := canvas shapes copyFrom: 1 to: 3.
canvas open.
canvas camera 
	zoomToFit: canvas extent*0.8 
	rectangle: group encompassingRectangle.
canvas signalUpdate

In previous example there is a group with the first 3 shapes, then we open the window, this creates the canvas and sets its extent. With zoomToFil:rectangle: we change the position and the scale of the camera to fit in the 80% of the canvas extent, the rectangle that contains all the boxes of the group. This changes the canvas camera but it does not notify the change, thats why we need the signalUpdate. to refresh the canvas view.

Shapes by layer

RSCanvas has 2 big groups shapes and fixedShapes first canvas draws the group shapes then draws the group fixedShapes.

Shapes

This is a generic list of shapes, the first shape in this collection is the first one to be draw in the canvas. The camera of the canvas applies its transformation to this collection of shapes, moving the origin to the middle of the canvas.

canvas := RSCanvas new.
canvas addShape: (RSLabel new text: 'Foo').
canvas addShape: (RSLine new 
	startPoint: -100@0;
	endPoint: 100@0 ).
"this box will be hide the label"
canvas addShape: (RSBox new size: 50).

canvas shapes size.
"3"
canvas.

Fixed shapes

This collection of shapes are not affected by the canvas camera, that means that their positions are on the corner of the canvas. Fixed shapes are drawn after Shapes. Use method isFixed:true to add a shape to the fixed group.

canvas := RSCanvas new.
canvas addShape: (RSBox new 
	size: 50;
	position: 50 asPoint).

canvas addShape: (RSBox new
	isFixed: true;
	size: 50;
	color: Color red;
	position: 50 asPoint). 
canvas addShape: (RSLine new
	isFixed: true;
	startPoint: 50 asPoint;
	endPoint: 100 asPoint). 
"This will change the camera scale
and will modify the size of the first box"
canvas camera scale: 0.2.
canvas.

Nodes

You can access to this group with the method add: with an instance of RSBoundingShape or with or addAll: and a collection of instances of RSBoundingShape.

A valid node can be a RSBitmap, RSBox, RSEllipse, RSComposite, RSLabel, RSPolygon, RSPieSlice or RSSSVGPath

canvas := RSCanvas new.

canvas add: RSBox new.
canvas addAll: ((1 to: 20) collect: [ :i | 
	RSBox new
		size: i;
		color: Color black;
		position: i* 10 asPoint] ).
"this changes the position and extent for the first box"
canvas nodes first
	fromRectangle: canvas encompassingRectangle.
canvas zoomToFit.
canvas.
### Edges

Like before Roassal uses this collection to know each instance of RSAbstractLine added to the canvas with the method add: or addAll:.

A valid edge can be RSBezier, RSPolyline, RSLine, RSSVGPathLine

canvas := RSCanvas new.

canvas addAll: ( (1 to: 20 by: 2)  collect: [ :i|
	RSLine new
		startPoint: 0@ i;
		endPoint: 100@ i;
		yourself ]  ).
canvas edges size.
"10"
canvas zoomToFit.
canvas.

Creating edges can be complicated, then you should take a look to the RSEdgeBuilder.

Canvas Events

The canvas as their shapes react to certain events, like the extent changed, or when the camera change position or scale.

RSExtentChangedEvent is called each time that the canvas morph changes its extent. RSScaleChangedEvent is called when the camera scale changes its value. RSPositionChangedEvent is called each time that the camera position is changed.

canvas := RSCanvas new.
label := RSLabel new.
canvas add: label.
canvas 
	when: RSExtentChangedEvent 
	do: [ :evt | 
		label text: 'Extent: ', evt newExtent asString.
		canvas zoomToFit; signalUpdate. ];
	when: RSScaleChangedEvent
	do: [ :evt | 
		label text: label text, ' Scale: ', evt newScale asString].
canvas 

RSMorph

At this phase Roassal3 only runs on pharo, in order to communicate with the pharo ecosystem, RSCanvas defines a morph an instance of RSMorph, the visual component for Pharo, this morph sends the events the drawing cycle to the RSAthensRenderer. Usually the user creates inderectly this morph when he opens the canvas or when he uses the inspector.

canvas := RSCanvas new.

canvas addAll: ( (1 to: 20 by: 2)  collect: [ :i|
	RSLine new
		startPoint: 0@ i;
		endPoint: 100@ i;
		yourself ]  ).
canvas edges size.
"10"
canvas zoomToFit.
morph := canvas createMorph.
root := Morph new.
root addMorph: morph.
morph extent: 300@300.
root color: Color black.
(root openInWindowLabeled: '2020') position: 100 asPoint.

In this example we can use Morphic to create a simple user interface. You can use SpRoassalPresenter and Spec2 from Pharo to build complex user interfaces. Use Sp1RoassalPresenter in Pharo7 for spec1.

Clone this wiki locally