Day 2
Let’s Place the Dots in a Circle
Today we are going to learn about
SVG transformations
SVG groups (?)

The Problem

Previously we have created a program that displays one dot in the center of the screen. Today we want to place five dots in a circle, like this:

Multiple Dots

We will start from where we left off yesterday. In the browser go to your saved Ellie bookmark. If you run it you should notice first obvious difference. Currently we only have one dot. It’s the one created using
Svg.circle
function on lines 9 - 15, this code fragment:
Elm
1 - 8
unfold
9
[ Svg.circle
10
[ Svg.Attributes.r "10"
11
, Svg.Attributes.cx "0"
12
, Svg.Attributes.cy "0"
13
, Svg.Attributes.fill "skyblue"
14
]
15
[]
16
]
17 - 29
unfold
If you look closely in your source code, the block above is surrounded by
[
and
]
characters. That’s a list. We will talk about lists during the next day. For now it’s enough to say that a list can contain zero, one or more items of the same type. In this case it contains one item of type
Svg msg
- our lonely, blue dot. Let’s add a second one like this:
Elm
1 - 7
unfold
8
main =
9
[ Svg.circle
10
[ Svg.Attributes.r "10"
11
, Svg.Attributes.cx "0"
12
, Svg.Attributes.cy "0"
13
, Svg.Attributes.fill "skyblue"
14
]
15
[]
16
, Svg.circle
17
[ Svg.Attributes.r "10"
18
, Svg.Attributes.cx "0"
19
, Svg.Attributes.cy "0"
20
, Svg.Attributes.fill "orange"
21
]
22
[]
23
]
24 - 36
unfold
Now the list spans from lines 9 to 23 and contains two items: skyblue and orange dots. The result should be:
Oh oh! The list contains two dots, but we can only see the orange one on the screen. Where is the blue one? It’s behind the orange. Recall how the cartesian coordinates work: if
x
and
y
of the center are the same, then the dots are in the same place. If we want to see both dots, let’s move one of them to a different place, like this:
Elm
1 - 7
unfold
8
main =
9
[ Svg.circle
10
[ Svg.Attributes.r "10"
11
, Svg.Attributes.cx "0"
12
, Svg.Attributes.cy "0"
13
, Svg.Attributes.fill "skyblue"
14
]
15
[]
16
, Svg.circle
17
[ Svg.Attributes.r "10"
18
, Svg.Attributes.cx "50"
19
, Svg.Attributes.cy "0"
20
, Svg.Attributes.fill "orange"
21
]
22
[]
23
]
24 - 35
unfold
Now you should see this:
We can add more dots, by simply multiplying the code blocks inside the list. Each block must be separated from others with a
,
(comma). Each time the
cx
and
cy
coordinates need to be different. We can also give dots different colors. Make five of them.

What does it mean to be placed on a circle?

We all have some intuition about a circle. You can tell if you see one and if you have a steady hand, you can probably draw one with a pencil. It’s a shape that looks like this:
But what is it that makes a circle what it is? First of all we have to realize that every circle has a center. It’s a point that lays exactly in the middle:
We can say that dots lay on a circle, if the distance to the center is the same for each of them, like this:
A mathematician would say:
Four or more points belong to a circle when there is a point (called center) that is equally distant from each of the points.
That is more precise, but perhaps a little more difficult to visualize.
We call this distance
a
radius
of a circle
. We already saw radius used as an attribute of a circle when we were making our first dot. Remember:
Elm
1
Svg.circle
2
[ Svg.Attributes.r "10"]
3
[]
4
5
The
r
attribute stands for radius. Then it was basically dictating the size of the dot. The larger the radius the bigger the circle. A dot is just a filled circle!
radius = 40
radius = 60
radius = 80
This time the radius will affect the size of an imaginary circle on which the dots are laying.
Ok, so this describes what it means to lay on a circle. But what are some efficient methods of putting things there? How about the following.
You choose a center (anywhere, say
(0, 0)
) and radius (any length you like, say
80
).
Take a ruler and place it’s one end (the one showing the value 0) at the center point. Then move the thing you want to place along the ruler the length you chose. Leave it there.
Next slightly rotate the ruler around the center (keep point
0
of the ruler at point
(0, 0)
). Repeat the steps above with another thing. Move it along the slider the same length as previously (radius). Because this time the ruler points in different direction, the thing will end up in a different place.
TODO: An interactive example with ruler. Basically a modified Translations.
In other words, you can put a thing in the center and then move it in any direction, let’s say 1 meter. If you do the same to several things in several directions (but same length), you will make them lay on a circle. The direction must be different each time. Otherwise the things would just stack one on top of another! We wouldn’t call it a circle, right?
Now, notice that the dots displayed by the program we are creating are not laying just anywhere on the circle. They are evenly distributed. It means that not only the distance between each dot and the center is the same. Also each dot lay the same distance away from it’s two nearest neighbors. Just compare the following two pictures:
On the right, you immediately see a pattern. They form a circle. But in fact both pictures are showing 5 dots laying on a circle. Look:
IDEA: An interactive program (game of sorts) that shows a ruler and dots. User can drag the ruler around and rotate it. Also dots can be dragged around. Once the dots are placed on a circle, the program rewards the user with a nice animation and message. It would be super awesome to make it touch capable
Then second variety. This time there is also a compass tool. User has to place the dots on a circle so that they are evenly distributed.
In the mean time we have two non-interactive examples with evenly and randomly distributed dots.
So the dots on the left lay on a circle, but there is a striking difference. They are all scattered! This means that there is something missing in our solution. We need to take care of one more aspect when we place our dots.
Since the center and radius is the same for each dot, the only thing we can play with is the angle that we rotate the ruler each time.
A common way of representing rotation is in degrees. It works like this: imagine a circle around you. You are in the center. Now imagine that the circle is divided in 360 equal segments. If you turn from one segment to the next one on the right (a very small turn) then you have turned one degree. If you turn around to face the opposite direction, that’s a turn of 180 degrees. Do it again and you are facing the same direction as before (a 360 degree turn). If you turn to the right (as on a corner of a street), then you have turned 90 degrees (half of 180, quarter of 360).
In physical world we often use a tool called protractor to measure angles. It typically has one degree segments already marked for you. It looks like this:
0102030405060708090100110120130140150160170180190200210220230240250260270280290300310320330340350
Now if we make the turns of the ruler equal between placing each dot, then the dots will be placed evenly. Equal turns mean that the number of degrees covered by each turn will be the same, including the turn you would need to make to go from the last dot back to the first. But how many degrees shall it be?
In mathematics there is a special operation to determine it. Perhaps you have heard about it: it’s called division and is written like that:
360 / 5
Maybe you can solve it in your head, but if not, why not use an online Elm REPL? Go to
elmrepl.cuberoot.in
.
And then type
360 / 5
and press
enter
. You should see:
Elm Repl
---- Elm 0.19.0 ----------------------------------------------------------------
Read <https://elm-lang.org/0.19.0/repl> to learn more: exit, help, imports, etc.
--------------------------------------------------------------------------------
> 360 / 5
72 : Float
>
Turns out that the result is 72. That’s the number of degrees we have to turn our ruler around the center each time we place a new dot. If you have a protractor like the one pictured above, you can use it to measure the turn. Then measure the distance from the center along the ruler (a length called radius) and place the dot there.
0102030405060708090100110120130140150160170180190200210220230240250260270280290300310320330340350

The Code

Now that we understand what it means to be placed on a circle and evenly distributed, it’s time to create a program that displays dots like that. Once again, this is the intended result:
Let’s get back to our code in Ellie. It should look something like this:
Elm
1
module Main exposing (main)
2
3
import Element
4
import Svg
5
import Svg.Attributes
6
7
8
main =
9
[ Svg.circle
10
[ Svg.Attributes.r "10"
11
, Svg.Attributes.cx "0"
12
, Svg.Attributes.cy "0"
13
, Svg.Attributes.fill "skyblue"
14
]
15
[]
16
, Svg.circle
17
[ Svg.Attributes.r "10"
18
, Svg.Attributes.cx "40"
19
, Svg.Attributes.cy "0"
20
, Svg.Attributes.fill "orange"
21
]
22
[]
23
, Svg.circle
24
[ Svg.Attributes.r "10"
25
, Svg.Attributes.cx "80"
26
, Svg.Attributes.cy "0"
27
, Svg.Attributes.fill "red"
28
]
29
[]
30
, Svg.circle
31
[ Svg.Attributes.r "10"
32
, Svg.Attributes.cx "120"
33
, Svg.Attributes.cy "0"
34
, Svg.Attributes.fill "lime"
35
]
36
[]
37
, Svg.circle
38
[ Svg.Attributes.r "10"
39
, Svg.Attributes.cx "160"
40
, Svg.Attributes.cy "0"
41
, Svg.Attributes.fill "maroon"
42
]
43
[]
44
]
45
|> Svg.svg
46
[ Svg.Attributes.height "100%"
47
, Svg.Attributes.width "100%"
48
, Svg.Attributes.viewBox "-300 -300 600 600"
49
]
50
|> Element.html
51
|> Element.layout
52
[ Element.width Element.fill
53
, Element.height Element.fill
54
]
55
56
57
At this point there may be some differences in colours and positions of the dots, but if you follow the instructions so far, the code should look familiar.
It’s a good starting point. We already have five dots, one of them in the center (
cx
and
cy
set to
0
). Let’s say this will also be the center of our circle. Move all the other dots there. Now it’s time to introduce our protractor and ruler. These is a property of an SVG element called `transform`. You use it like that:
Svg.Attributes.transform "translate(80)"
This will move the dot (“translate” is a fancy term for “move”) 80 units.
Elm
1 - 11
unfold
12
[ Svg.circle
13
[ Svg.Attributes.r "10"
14
, Svg.Attributes.cx "0"
15
, Svg.Attributes.cy "0"
16
, Svg.Attributes.transform "translate(80)"
17
, Svg.Attributes.fill "skyblue"
18
]
19
[]
20
, Svg.circle
21
[ Svg.Attributes.r "10"
22
, Svg.Attributes.cx "0"
23
, Svg.Attributes.cy "0"
24
, Svg.Attributes.fill "orange"
25
]
26
[]
27 - 55
unfold
With this change, click
COMPILE
. You should see something like this:
Looks like two dots, but we are not going to fall for this again. We know that there are four of them stacked in the center (with the maroon one on top). What’s interesting is the blue one. It’s moved!
Now let’s move the second dot.
Elm
1 - 11
unfold
12
[ Svg.circle
13
[ Svg.Attributes.r "10"
14
, Svg.Attributes.cx "0"
15
, Svg.Attributes.cy "0"
16
, Svg.Attributes.transform "translate(80)"
17
, Svg.Attributes.fill "skyblue"
18
]
19
[]
20
, Svg.circle
21
[ Svg.Attributes.r "10"
22
, Svg.Attributes.cx "0"
23
, Svg.Attributes.cy "0"
24
, Svg.Attributes.transform "translate(80)"
25
, Svg.Attributes.fill "orange"
26
]
27 - 57
unfold
Of course it will end up on top of the blue one. That’s not what we want. We need to change the direction of the movement. Let’s rotate it 72 degree before we translate, like this:
Elm
1 - 11
unfold
12
[ Svg.circle
13
[ Svg.Attributes.r "10"
14
, Svg.Attributes.cx "0"
15
, Svg.Attributes.cy "0"
16
, Svg.Attributes.transform "translate(80)"
17
, Svg.Attributes.fill "skyblue"
18
]
19
[]
20
, Svg.circle
21
[ Svg.Attributes.r "10"
22
, Svg.Attributes.cx "0"
23
, Svg.Attributes.cy "0"
24
, Svg.Attributes.fill "orange"
25
, Svg.Attributes.transform "rotate(72) translate(80)"
26
]
27
[]
28
, Svg.circle
29 - 56
unfold
This will rotate the element by 72 degrees. On its own it wouldn’t make any difference, because the dot is round - it doesn’t matter how you rotate it, it always looks the same. But It also rotates its internal coordinates system. So the translation will move it in a different direction then the first dot. It’s like rotating the ruler before moving the dot along it’s edge. Here is the expected result:
Now it’s time to move the fird and the rest of the dots. Red one gets rotated
144
degree, next one
216
and finally
288
. Why
144
? It’s because
72 + 72 is 144
! So to be
72
degree apart from a dot that’s at
72
degree, you need to be at
144
(or
0
, but the blue dot is already there). Note that
288 + 72 = 360
, so the distance between the first and last dot is correct. The beauty of math 🤓
The complete program should look like this:
Elm
1
module Main exposing (main)
2
3
import Element
4
import Svg
5
import Svg.Attributes
6
7
8
main =
9
Svg.svg
10
[ Svg.Attributes.viewBox "-100 -100 200 200"
11
]
12
[ Svg.circle
13
[ Svg.Attributes.r "10"
14
, Svg.Attributes.cx "0"
15
, Svg.Attributes.cy "0"
16
, Svg.Attributes.transform "translate(80)"
17
, Svg.Attributes.fill "skyblue"
18
]
19
[]
20
, Svg.circle
21
[ Svg.Attributes.r "10"
22
, Svg.Attributes.cx "0"
23
, Svg.Attributes.cy "0"
24
, Svg.Attributes.fill "orange"
25
, Svg.Attributes.transform "rotate(72) translate(80)"
26
]
27
[]
28
, Svg.circle
29
[ Svg.Attributes.r "10"
30
, Svg.Attributes.cx "0"
31
, Svg.Attributes.cy "0"
32
, Svg.Attributes.fill "red"
33
, Svg.Attributes.transform "rotate(144) translate(80)"
34
]
35
[]
36
, Svg.circle
37
[ Svg.Attributes.r "10"
38
, Svg.Attributes.cx "0"
39
, Svg.Attributes.cy "0"
40
, Svg.Attributes.fill "lime"
41
, Svg.Attributes.transform "rotate(216) translate(80)"
42
]
43
[]
44
, Svg.circle
45
[ Svg.Attributes.r "10"
46
, Svg.Attributes.cx "0"
47
, Svg.Attributes.cy "0"
48
, Svg.Attributes.fill "maroon"
49
, Svg.Attributes.transform "rotate(288) translate(80)"
50
]
51
[]
52
]
53
|> Element.html
54
|> Element.layout
55
[ Element.width Element.fill
56
, Element.height Element.fill
57
]
58
59
In Ellie it should look exactly as we planned:
Congratulations! You are ready for
Day 3
!