Skip to content
This repository has been archived by the owner on Sep 12, 2024. It is now read-only.

3. Update shapes properties (Colors, Transformations, ...)

Yann LE GOFF edited this page Aug 17, 2022 · 11 revisions

This section will present the differents "basics" properties of a CadeShape. We will see:

  • How to change the color of a shape.
  • How to change the stroke (border).
  • How to move a shape.
  • How to rotate a shape.
  • How to apply a scale transformation.
  • How to hide / show a shape.
  • How to allow / disallow interactions.
  • How to bufferised a shape.

Change the color of a shape

To change the color that fill the shape you have to use the method fillColor:. We will see different way to use this method:

  • with Color
  • with AthensCairoGradientPaint

With Color

We can directly use the default color object of Pharo.

view := CadeView new open.

"Create shapes and add them to the view."
shape1 := CadeShapeLeaf2D shape: (0@0 extent: 200@200).
view addShape: shape1.
shape2 := CadeShapeLeaf2D shape: (50@50 extent: 200@200).
view addShape: shape2.
shape3 := CadeShapeLeaf2D shape: (100@100 extent: 200@200).
view addShape: shape3.
shape4 := CadeShapeLeaf2D shape: (150@150 extent: 200@200).
view addShape: shape4.

"Simple blue color"
shape1 fillColor: (Color blue).

"From a hexadecimal value"
shape2 fillColor: (Color fromHexString: 'B00778').

"From RGB value"
shape3 fillColor: (Color r: 0.1 g: 0.2 b: 0.3).

"With transparency added"
shape4 fillColor: (Color green alpha: 0.3).

image

With AthensCairoGradientPaint

We use AthensCairoGradientPaint to create a soft transition between multiple Color in Pharo. There is two type of gradiant we can make: linear or radial.

Example with linear gradient:

view := CadeView new open.

shape := CadeShapeLeaf2D shape: (0@0 extent: 400@400).
view addShape: shape.

color := AthensCairoGradientPaint
    createLinearGradient: { 
	(0 -> Color red).
	(0.2 -> Color pink).
	(0.4 -> Color white).
	(0.6 -> Color cyan).
	(0.8 -> Color blue).
	(1 -> Color yellow)}
    start: 0@0
    stop: 0@400.

shape fillColor: color.

image image

Example with radial gradient:

view := CadeView new open.

shape := CadeShapeLeaf2D shape: (0@0 extent: 400@400).
view addShape: shape.

color := AthensCairoGradientPaint
    radialBetween: 200 @ 150
    extending: 250
    and: 0 @ 0
    extending: 0
    withColorRamp: { 
        (0 -> Color black).
        (0.1 -> Color blue).
        (0.5 -> Color red).
        (1 -> Color yellow) }.

shape fillColor: color.

image image

Change the stroke (border) of a shape

There are 2 methods to change the stroke arround a shape:

  • strokeColor: to just add a simple stroke arround the shape with a default size of 1 pixel.
  • strokeColor:width: to add a stroke and to precise it's size.

You can add any color you want from Color or AthensCairoGradientPaint. Important to note, the interraction of a stroked shape is arround the shape and not arround the shape and the stroke.

view := CadeView new open.

default := CadeShapeLeaf2D shape: (CadeCircle center: 100@200 radius: 25).
default fillColor: (Color blue).
view addShape: default.

shape1 := CadeShapeLeaf2D shape: (CadeCircle center: 200@100 radius: 50).
shape1 fillColor: (Color red).
view addShape: shape1.

shape2 := CadeShapeLeaf2D shape: (CadeCircle center: 200@300 radius: 50).
shape2 fillColor: (Color red).
view addShape: shape2.

"We stroke the shapes"
default strokeColor: Color black.
shape1 strokeColor: Color black width: 40. 
shape2 strokeColor: Color black translucent width: 40.

"A bit of interraction on shape2"
shape2 onEvent: #mouseEnter executeAction: [ shape2 fillColor: Color yellow ].
shape2 onEvent: #mouseExit executeAction: [ shape2 fillColor: Color red ].

Pharo_VvQdgUwIRU

Move the position of a shape

It' possible to change the position of a shape in the model with different methods:

  • x: to change the horizontal position of a shape.
  • y: to change the vertical position of a shape.
  • coordinates: to change the horizontal and the vertical position of a shape.

For example, lets start by creating a simple hierachy of shapes. In a playground execute the following code:

view := CadeView new open.

"A container"
container1 := CadeShapeContainer new.
view addShape: container1.

bg1 := CadeShapeLeaf2D shape: (0@0 extent: 200@200).
bg1 fillColor: (Color blue).
container1 addShape: bg1.

"A container inside the first container"
container2 := CadeShapeContainer new.
container1 addShape: container2.

bg2 := CadeShapeLeaf2D shape: (0@0 extent: 100@100).
bg2 fillColor: (Color red).
container2 addShape: bg2.

shape := CadeShapeLeaf2D shape: (0@0 extent: 20@20).
shape fillColor: (Color yellow).
container2 addShape: shape.

image image

The container1 with a blue background contains a container2 with the red background. The container2 contains a shape in yellow. All poistion start at 0@0.

We will use the methods coordinates to move the container1 in the center of the view. In the same playground execute the following code:

container1 coordinates: 100@100.

image

All the shapes inside the container1 moved. Then we will move the yellow shape a bit to the right.

shape x: 60.

image

The shape moved 60 pixel to the right. In fact if we inspect the result of the method shape coordinates. we can see that the coordinate of the shape is now 60.0@0. Note that it didn't added the first translation of the container1 of 100 pixel. The coordinates are always relatives to the direct parent of a shape. To prove it, we will move the container 2 on the horizontal axis.

container2 y: -50.

image

If we inspect the different coordinates of every shapes we now have:

  • container1100.0@100.0
  • bg10 @ 0
  • container20@ -50.0
  • bg20 @ 0
  • shape60.0 @ 0

Add a rotation to a shape

In this part, we will see 2 methods. angleInDegree: and rotationPoint:. To illustrate this methods we will use this example:

view := CadeView new open.

"A container"
container1 := CadeShapeContainer new.
container1 coordinates: 100@150.
view addShape: container1.

bg1 := CadeShapeLeaf2D shape: (0@0 extent: 200@100).
bg1 fillColor: (Color blue).
container1 addShape: bg1.

"A container inside the first container"
container2 := CadeShapeContainer new.
container2 coordinates: 50@25.
container1 addShape: container2.

bg2 := CadeShapeLeaf2D shape: (0@0 extent: 100@50).
bg2 fillColor: (Color red).
container2 addShape: bg2.

shape := CadeShapeLeaf2D shape: (0@0 extent: 20@10).
shape fillColor: (Color yellow).
shape coordinates: 40@20.
container2 addShape: shape.

image image

First let's see angleInDegree: on container1:

container1 angleInDegree: 25.

The shape rotate, and all it's children, from the origin point (0 @ 0) of the shape. We can change the origin of the rotation with the method: rotationPoint:.

"We change the rotation angle to something more pronounced."
container1 angleInDegree: 90.

"We will put the rotation point on the center of the shape."
container1 rotationPoint: 100@50.

--- Before changing the rotation point ---

Pharo_mRyYh9BqsD VBdxUw43Xw

--- After changing the rotation point ---

Pharo_2qoTxhs4k8 vBgKdSln9f

Let's see what happen when we change the rotation angle inside multiple containers. With the previous example:

container1 angleInDegree: 45.
container2 angleInDegree: -90.
shape angleInDegree: 45.

The blue square should be in diagonal (angle of 45°). The red square should be perpendicular to the blue square (angle of -90°). The yellow square should look flat (45° + -90° + 45° = 0°).

image

Change the scale of a shape

Cade allow to change the scale of a shape. we have 2 methods to influe on the scale: scale: and scaleCenter:. Let's see them in action with the previous example.

view := CadeView new open.

"A container"
container1 := CadeShapeContainer new.
container1 coordinates: 100@150.
view addShape: container1.

bg1 := CadeShapeLeaf2D shape: (0@0 extent: 200@100).
bg1 fillColor: (Color blue).
container1 addShape: bg1.

"A container inside the first container"
container2 := CadeShapeContainer new.
container2 coordinates: 50@25.
container1 addShape: container2.

bg2 := CadeShapeLeaf2D shape: (0@0 extent: 100@50).
bg2 fillColor: (Color red).
container2 addShape: bg2.

shape := CadeShapeLeaf2D shape: (0@0 extent: 20@10).
shape fillColor: (Color yellow).
shape coordinates: 40@20.
container2 addShape: shape.

"We change the scale center. We will see after what it does."
container1 scaleCenter: 100@50.

Lets modify the scale of the first container. By default the scale is 1@1.

"This should do nothing."
container1 scale: 1@1.

"This should double the width."
container1 scale: 2@1.

"This should divide the height by 2."
container1 scale: 1@0.5.

--- Normal scale (1 @ 1) ---

image

--- Double width (2 @ 1) ---

image

--- Divide the height by 2 (1 @ 0.5) ---

image

The method scaleCenter: allow us to change the origin of the growth of a shape. For example if we set the scale center in the bottom left of a shape it should grow toward the top right. If we sttle it in the center (like in the previous example it should grow in all direction.

--- Top left ---

"Top left"
container1 scaleCenter: 0 @ 0.
container1 scale: 4.0 @ 4.0.

image

--- Top right---

"Top right"
container1 scaleCenter: 200 @ 0.
container1 scale: 4.0 @ 4.0.

image

--- Bottom right ---

"Bottom right"
container1 scaleCenter: 200 @ 100.
container1 scale: 4.0 @ 4.0.

image

--- Etc. ---

The scale is also reported to the children shapes. We will see an example that combine multiple transformations.

"First we double the height of the yellow shape."
shape scale: 1@2.

"Secondly we reduce by half the width of the container with the red shape and the yellow shape."
container2 scale: 0.5@1.

"Finally we double the height of the contianer that contain all the shapes."
"Pay attention to all shapes. The blue one will change but also the red and the yellow one."
container1 scale: 1@2.

Pharo_0b7oNVTAcz

Hide and show a shape

It's possible to hide a shape by setting the isVisible variable to false. By default this value is equal to true. We will see an example:

view := CadeView new open.

"Adding a background"
shape1 := CadeShapeLeaf2D shape: (0@0 extent: 100@100).
shape1 fillColor: (Color red).

"Adding a shape to display"
shape2 := CadeShapeContainer new
	addShape: (CadeCircle center: 50@20 radius: 10) asCadeShape;
	addShape: (45@20 extent: 10@45) asCadeShape;
	addShape: (25@35 extent: 50@10) asCadeShape;
	addShape: (35@60 extent: 10@30) asCadeShape;
	addShape: (55@60 extent: 10@30) asCadeShape.


view addShape: shape1.
view addShape: shape2.

Execute the following code in a playground then in the same playground we can change the isVisble value of one shape and see the result.

"select then ctrl+d"
shape2 isVisible: false

Pharo_hZ67zgcS4c

Allow or disallow interactions

It is possible to allow or dissalow interractions on a shape by setting the value isEnabled of a shape to false.

view := CadeView new open.

"Adding a basic shape to the view"
bg := CadeShapeLeaf2D shape: (0@0 extent: 300@300).
bg fillColor: (Color blue).

button := CadeShapeLeaf2D shape: (0@0 extent: 200@50).
button fillColor: Color gray.
button coordinates: 50@125.

"button interractions"
button action: [ Transcript show: 'click ' ].
button onEvent: #mouseEnter executeAction: [ button fillColor: Color gray muchLighter ].
button onEvent: #mouseExit executeAction: [ button fillColor: Color gray ].

container := CadeShapeContainer new
	addShape: bg;
	addShape: button.

view addShape: container.

If you want to dissalow the interraction on the button you can set the isEnabled value to false on the button or any parent shape of the button.

button isEnabled: false.
"or"
container isEnabled: false.

Pharo_WQtgVjN3EF

Create a buffer for a shape

It is possible to create a buffer for a shape.

If a shape is NOT in a buffer and a part of it need to be redraw. Cade will redraw every visible pixel of the shape. If a shape is inside a buffer and a part of it need to be redraw, it will redraw only the part that needed.

Putting the shape inside a buffer decrease the number of opération needed to redraw the shape and increase significativly the performance of the application. To increase even more the performance you can disble the event of the shape by setting the isEnebled value to false.

Next an example of a buffered shape:

view := CadeView new open.

"Creating a container containning 10 000 shapes"
random := Random new.
bg := CadeShapeContainer new.
1 to: 10000 do: [ :i | | _shape |
	_shape := (-5 @ -5 extent: 10 @ 10) asCadeShape.
	_shape fillColor: Color random.
	_shape x: random next * 400.
	_shape y: random next * 400.
	bg addShape: _shape.
].
bg isEnabled: false.
bg isBuffered: true.

overlay := CadeShapeLeaf2D shape: (CadeCircle center: 0@0 radius: 30).
overlay fillColor: Color black.
overlay coordinates: 200@200.
overlay isDraggable: true.

view addShape: bg.
view addShape: overlay.

Pharo_38HxE6duiX

The buffer is created by the engine. Currently the only engine working with Cade is the CairoEngine. A buffered shape by the CairoEngine will first create new AthensCairoSurface and draw itself on it. Then the surface will be saved as a Ressource in the shape. The engine will then display every shapes. If a shape is inside a buffer it will simply draw the pre-drawn surface inside the Ressource.

When a buffered shape need to be redraw, a special signal is send to the shape and the Ressource is redraw. When the child of a buffered shape need to be redraw, it will trigger an event to its parent.