Commits: 13

Make the Dot example work, add fill property to Container

Day 1 is updated to work with Dots. Day 2 needs to get updated still.

index d1fc7a3..680cd91 100644
--- a/content/day-1.txt
+++ b/content/day-1.txt
@@ -111,10 +111,18 @@ This was a warmup. Let's try something more challenging.
=We want to display a dot at the center of the screen, like this:
=
=| Window
-    | DotWithViewBox
-        background=none
-        fill=skyblue
-        viewBox=-300 -300 600 600
+    | Dots
+        | Container
+            background = none
+            viewBox = -300 -300 600 600
+            fill = True
+
+
+        | Dot
+            cx = 0
+            cy = 0
+            color = skyblue
+            radius = 10
=
=Below is the complete code to draw a dot like this, but don't type it in your editor yet! We are going to recreate it together, step by step.
=
@@ -193,9 +201,17 @@ Start the reactor again by entering:
=Reload the browser. You should see something like this:
=
=| Window
-    | Dot
-        background=none
-        fill=black
+    | Dots
+        | Container
+            background = none
+            fill = False
+            viewBox = auto
+
+        | Dot
+            radius = 10
+            cx = 0
+            cy = 0
+            color = black
=
=At this point we have a dot on the screen - it's at the top left corner of the viewport (the area of the browser window where the content is displayed). We can see only a quarter of it - the rest is outside of the viewport.
=
@@ -220,9 +236,17 @@ First we have to realize that the dot is inside an {Code|svg} element that itsel
=Reloading the browser should reveal something like this:
=
=| Window
-    | Dot
-        background=pink
-        fill=black
+    | Dots
+        | Container
+            background = pink
+            fill = False
+            viewBox = auto
+
+        | Dot
+            radius = 10
+            cx = 0
+            cy = 0
+            color = black
=
=Since the dot is inside the {Code|svg} element, and the {Code|svg} element doesn't cover the center of the screen, it's impossible to place the dot at the center. Before anything else, let's make the {Code|svg} fill entire {Definition|term=viewport|definiens=the area of the browser window where the content is displayed}.
=
@@ -271,11 +295,17 @@ As before, press {Key|enter} to follow the plan. Now let's use the package. Chan
=Reload the browser. The SVG space now fills the entire viewport. Even the small white margin is gone!
=
=| Window
-    | DotInElement
-        background=pink
-        fill=black
-        cx=0
-        cy=0
+    | Dots
+        | Container
+            background = pink
+            fill = True
+            viewBox = auto
+
+        | Dot
+            radius = 10
+            cx = 0
+            cy = 0
+            color = black
=
=It should now be possible to place our dot in the center of the screen. Currently the dot is in the top left corner of the viewport, but at least the parent {Code|svg} element is covering it, so the center of the viewport is somewher within the {Code|svg} element.
=
@@ -334,11 +364,17 @@ To change the position of the dot, we can use the {Code|cx} and {Code|cy} attrib
=            ]
=
=| Window
-    | DotInElement
-        background=pink
-        fill=black
-        cx=100
-        cy=50
+    | Dots
+        | Container
+            background = none
+            fill = True
+            viewBox = auto
+
+        | Dot
+            radius = 10
+            cx = 100
+            cy = 50
+            color = black
=
=| Note
=    The {Code|cx} stands for /center x/ and {Code|cy} for /center y/, where center refers to the circle. We will discuss what the center of the circle is in more exact terms on day 2, but you probably get the concept.
@@ -415,10 +451,17 @@ In the code we set the viewbox as an attribute of the {Code|svg} element, like t
=There are only two changes here. First we reset the {Code|cx} and {Code|cy} attributes of the {Code|circle} back to {Code|"0"} (on lines 11 and 12). We want the dot to be back at origin. Then we set up the position of the viewbox on line 20. Try experimenting with different values. If you set them right and refresh the broswer the dot should be in the center of the screen.
=
=| Window
-    | DotWithViewBox
-        background=pink
-        fill=black
-        viewBox=-300 -300 600 600
+    | Dots
+        | Container
+            background = pink
+            fill = True
+            viewBox = -300 -300 600 600
+
+        | Dot
+            radius = 10
+            cx = 0
+            cy = 0
+            color = black
=
=You can also remove line 19 to remove the pink background, or set it to some other color.
=
@@ -458,10 +501,17 @@ Speaking of colors, now that we've centered our dot, it's time to give it a colo
=The only change is on line 13. Reload the browser and see this:
=
=| Window
-    | DotWithViewBox
-        background=none
-        fill=skyblue
-        viewBox=-300 -300 600 600
+    | Dots
+        | Container
+            background = none
+            fill = True
+            viewBox = -300 -300 600 600
+
+        | Dot
+            radius = 10
+            cx = 0
+            cy = 0
+            color = skyblue
=
=| Emphasize
=    Wow!
index 4ebd806..e4d98a5 100644
--- a/src/Examples.elm
+++ b/src/Examples.elm
@@ -3,12 +3,9 @@ module Examples exposing (Model, Msg(..), init, subscriptions, update)
=import Examples.CartesianCoordinates
=import Examples.Circle
=import Examples.Counter
-import Examples.Dot as Dot
-import Examples.DotInElement as DotInElement
-import Examples.DotWithViewBox as DotWithViewBox
+import Examples.Dots
=import Examples.Gradient
=import Examples.Line
-import Examples.MultipleDots as MultipleDots
=import Examples.NestedTransformations
=import Examples.PolarCoordinates
=import Examples.RosetteTypedTransformations
deleted file mode 100644
index 5581546..0000000
--- a/src/Examples/Dot.elm
+++ /dev/null
@@ -1,32 +0,0 @@
-module Examples.Dot exposing (Config, defaults, ui)
-
-import Html exposing (Html)
-import Svg
-import Svg.Attributes
-
-
-type alias Config =
-    { background : String
-    , fill : String
-    }
-
-
-defaults : Config
-defaults =
-    Config "none" "black"
-
-
-main : Html msg
-main =
-    ui defaults
-
-
-ui : Config -> Html msg
-ui { background, fill } =
-    Svg.svg [ Svg.Attributes.style <| "background: " ++ background ]
-        [ Svg.circle
-            [ Svg.Attributes.r "10"
-            , Svg.Attributes.fill fill
-            ]
-            []
-        ]
deleted file mode 100644
index ee127b3..0000000
--- a/src/Examples/DotInElement.elm
+++ /dev/null
@@ -1,44 +0,0 @@
-module Examples.DotInElement exposing (Config, main, ui)
-
-import Element exposing (Element)
-import Svg
-import Svg.Attributes
-
-
-type alias Config =
-    { background : String
-    , fill : String
-    , cx : String
-    , cy : String
-    }
-
-
-defaults : Config
-defaults =
-    Config "pink" "black" "0" "0"
-
-
-main =
-    Element.layout
-        [ Element.width Element.fill
-        , Element.height Element.fill
-        ]
-        (ui defaults)
-
-
-ui : Config -> Element msg
-ui { background, fill, cx, cy } =
-    Svg.svg
-        [ Svg.Attributes.height "100%"
-        , Svg.Attributes.width "100%"
-        , Svg.Attributes.style <| "background: " ++ background
-        ]
-        [ Svg.circle
-            [ Svg.Attributes.r "10"
-            , Svg.Attributes.fill fill
-            , Svg.Attributes.cx cx
-            , Svg.Attributes.cy cy
-            ]
-            []
-        ]
-        |> Element.html
deleted file mode 100644
index 6f91dc6..0000000
--- a/src/Examples/DotWithViewBox.elm
+++ /dev/null
@@ -1,57 +0,0 @@
-module Examples.DotWithViewBox exposing (Config, main, ui)
-
-import Element
-import Svg
-import Svg.Attributes
-
-
-{-| This program demonstrates how to center an element within an SVG viewport. The idea is to make sure that (0, 0) point (so called origin) should be in the middle of the ViewBox. We can achive that, by making the top-left corner of the ViewBox same distance from the (0, 0) as the bottom right.
-
-The viewBox property takes a string consisting of four numbers:
-
-    - position of the top edge
-    - position of the left edge
-    - width
-    - and height
-
-If we set the width to 200 and then set the left edge to -100, that will place the right edge at +100. Both edges are 100 away from the 0!
-
-The same way you can control the bottom edge by tweaking the position of the top edge and the height.
-
-In fact it doesnt matter how wide and high the viewBox is. As long as the top will be -1/2 of the width and left will be -1/2 of the width.
-
-That's the trick to position an object (like a dot) exactly in the center of the SVG viewport
-
--}
-type alias Config =
-    { background : String
-    , fill : String
-    , viewBox : String
-    }
-
-
-defaults : Config
-defaults =
-    Config "white" "black" "-500 -500 1000 1000"
-
-
-main =
-    Element.layout
-        [ Element.width Element.fill
-        , Element.height Element.fill
-        ]
-        (ui defaults)
-
-
-ui { background, fill, viewBox } =
-    Svg.svg
-        [ Svg.Attributes.viewBox viewBox
-        , Svg.Attributes.style <| "background: " ++ background
-        ]
-        [ Svg.circle
-            [ Svg.Attributes.r "10"
-            , Svg.Attributes.fill fill
-            ]
-            []
-        ]
-        |> Element.html
index 5944489..4285cb8 100644
--- a/src/Examples/Dots.elm
+++ b/src/Examples/Dots.elm
@@ -10,6 +10,7 @@ import Circle2d
=import Element exposing (Element)
=import Geometry.Svg
=import Html exposing (Html)
+import Html.Attributes
=import Point2d exposing (Point2d)
=import Svg exposing (Attribute, Svg)
=import Svg.Attributes
@@ -24,6 +25,7 @@ type alias Config =
=type alias Container =
=    { background : String
=    , viewBox : String
+    , fill : Bool
=    }
=
=
@@ -40,6 +42,7 @@ defaults =
=    { container =
=        { background = "none"
=        , viewBox = "-300 -300 600 600"
+        , fill = True
=        }
=    , dots =
=        [ { cx = 0
@@ -69,13 +72,35 @@ ui { container, dots } =
=                |> Point2d.fromCoordinates
=                |> Circle2d.withRadius radius
=                |> Geometry.Svg.circle2d [ Svg.Attributes.fill color ]
+
+        width =
+            if container.fill then
+                "100%"
+
+            else
+                "300"
+
+        height =
+            if container.fill then
+                "100%"
+
+            else
+                "150"
+
+        padding =
+            if container.fill then
+                "0"
+
+            else
+                "5px"
=    in
=    dots
=        |> List.map dot
=        |> Svg.svg
=            [ Svg.Attributes.viewBox container.viewBox
-            , Svg.Attributes.style <| "background: " ++ container.background
-            , Svg.Attributes.width "100%"
-            , Svg.Attributes.height "100%"
+            , Html.Attributes.style "background" container.background
+            , Svg.Attributes.width width
+            , Svg.Attributes.height height
+            , Html.Attributes.style "margin" padding
=            ]
=        |> Element.html
deleted file mode 100644
index b0625da..0000000
--- a/src/Examples/MultipleDots.elm
+++ /dev/null
@@ -1,41 +0,0 @@
-module Examples.MultipleDots exposing (Config, main, ui)
-
-import Element
-import Svg exposing (Attribute)
-import Svg.Attributes
-
-
-type alias Config msg =
-    { background : String
-    , viewBox : String
-    , dotsAttributes : List (List (Attribute msg))
-    }
-
-
-defaults : Config msg
-defaults =
-    Config "white" "-500 -500 1000 1000" [ [ Svg.Attributes.r "10" ] ]
-
-
-main =
-    Element.layout
-        [ Element.width Element.fill
-        , Element.height Element.fill
-        ]
-        (ui defaults)
-
-
-ui { background, viewBox, dotsAttributes } =
-    Svg.svg
-        [ Svg.Attributes.viewBox viewBox
-        , Svg.Attributes.style <| "background: " ++ background
-        ]
-        (dotsAttributes
-            |> List.map
-                (\attributes ->
-                    Svg.circle
-                        attributes
-                        []
-                )
-        )
-        |> Element.html
index eae8b16..b2da4d8 100644
--- a/src/Main.elm
+++ b/src/Main.elm
@@ -22,7 +22,6 @@ import Examples
=import Examples.CartesianCoordinates
=import Examples.Circle
=import Examples.Counter
-import Examples.DotInElement
=import Examples.Dots
=import Examples.Gradient
=import Examples.Line
@@ -459,7 +458,6 @@ document =
=
=        examples =
=            [ counter
-            , dotInElement
=            , dots
=            , circle
=            , line
@@ -490,20 +488,6 @@ document =
=            in
=            Mark.stub "Counter" render
=
-        dotInElement : Mark.Block (Examples.Model -> Element Msg)
-        dotInElement =
-            let
-                render config model =
-                    Examples.DotInElement.ui config
-            in
-            Mark.record4 "DotInElement"
-                Examples.DotInElement.Config
-                (Mark.field "background" Mark.string)
-                (Mark.field "fill" Mark.string)
-                (Mark.field "cx" Mark.string)
-                (Mark.field "cy" Mark.string)
-                |> Mark.map render
-
=        dots : Mark.Block (Examples.Model -> Element Msg)
=        dots =
=            let
@@ -516,10 +500,11 @@ document =
=
=                container : Mark.Block Examples.Dots.Container
=                container =
-                    Mark.record2 "Container"
+                    Mark.record3 "Container"
=                        Examples.Dots.Container
=                        (Mark.field "background" Mark.string)
=                        (Mark.field "viewBox" Mark.string)
+                        (Mark.field "fill" Mark.bool)
=
=                dot : Mark.Block Examples.Dots.Dot
=                dot =
@@ -530,48 +515,13 @@ document =
=                        (Mark.field "radius" Mark.float)
=                        (Mark.field "color" Mark.string)
=            in
-            {- FIXME: This should really be:
-
-                   Mark.block "Dots"
-                       render
-                       (Mark.startWith
-                           Examples.Dots.Config
-                           container
-                           (Mark.manyOf [ dot ])
-                       )
-
-               and then markup:
-
-                   | Dots
-                       | Container
-                           background = brown
-                           viewBox = -200 -200 400 400
-
-                       | Dot
-                           cx = 10
-                           cy = 20
-                           color = blue
-                           radius = 33
-
-                       | Dot
-                           ...
-
-               but it produces parsing errors:
-
-                   End at 36:1
-                   Expecting newline at 36:1
-                   Block start at 36:1
-
-               TODO: Create a minimal reproducible example on Ellie and report issue at github.com/mdgriffith/elm-markup
-            -}
=            Mark.block "Dots"
-                (Examples.Dots.Config
-                    { background = "none"
-                    , viewBox = "-300 -300 600 600"
-                    }
-                    >> render
+                render
+                (Mark.startWith
+                    Examples.Dots.Config
+                    container
+                    (Mark.manyOf [ dot ])
=                )
-                (Mark.manyOf [ dot ])
=
=        circle : Mark.Block (Examples.Model -> Element Msg)
=        circle =

Merge branch 'content' into wip-dots-example

Add a little spacing between lines in the Terminal block

It's easier to read that way.

index 8430329..ddbec4e 100644
--- a/src/Mark/Custom.elm
+++ b/src/Mark/Custom.elm
@@ -235,6 +235,7 @@ terminal =
=                            ]
=                        , Font.color colors.gray
=                        , Element.scrollbarY
+                        , Element.spacing 6
=                        ]
=                ]
=    in

Write the section about rules based tree on day 4

index 3dbc52a..e5d8848 100644
--- a/content/day-4.txt
+++ b/content/day-4.txt
@@ -365,7 +365,6 @@ A segment within a segment! Consider that every segment can have child segments.
=    Time to play 🌳🌲🌴🎄
=
=| Emphasize
-
=    Try building your own tree this way. Play with colors and sizes.
=
=| Note
@@ -397,3 +396,372 @@ The tree following these rules will look like this:
=
=| Note
=    TODO: Example
+
+To represent this in code we will need to learn few new concepts: *dictionary*, *record*, *type alias* and *mapping over list*.
+
+Let's start with a record. Currently our {Code|segment} function takes three arguments first two are {Code|color : String} and {Code|rotation : Float}. The last one is a list of child segments. Instead we can merge the first two into one argument of type {Code|\{ color : String, rotation : Float \\}}. It will look like this:
+
+| Code
+    module Main exposing (main)
+
+    import Element
+    import Svg exposing (Svg)
+    import Svg.Attributes
+
+
+    main =
+        [ segment { color = "skyblue", rotation = 0 }
+            [ segment { color = "red", rotation = 15 } []
+            , segment { color = "blue", rotation = -15 }
+                [ segment { color = "yellow", rotation = 45 } []
+                , segment { color = "pink", rotation = -20 } []
+                , segment { color = "green", rotation = -90 } []
+                ]
+            ]
+        , segment { color = "orange", rotation = 72 } []
+        , segment { color = "red", rotation = 144 } []
+        , segment { color = "lime", rotation = 216 } []
+        , segment { color = "maroon", rotation = 288 } []
+        ]
+            |> Svg.svg
+                [ Svg.Attributes.height "100%"
+                , Svg.Attributes.width "100%"
+                , Svg.Attributes.style "background: none"
+                , Svg.Attributes.viewBox "-500 -500 1000 1000"
+                ]
+            |> Element.html
+            |> Element.layout
+                [ Element.width Element.fill
+                , Element.height Element.fill
+                ]
+
+
+    segment { color, rotation } children =
+        Svg.g []
+            [ Svg.g
+                [ Svg.Attributes.transform
+                    (String.concat
+                        [ "rotate("
+                        , String.fromFloat rotation
+                        , ") translate(80)"
+                        ]
+                    )
+                ]
+                children
+            , dot color rotation
+            , line color rotation
+            ]
+
+
+    dot color rotation =
+        Svg.circle
+            [ Svg.Attributes.r "10"
+            , Svg.Attributes.cx "0"
+            , Svg.Attributes.cy "0"
+            , Svg.Attributes.fill color
+            , Svg.Attributes.transform
+                (String.concat
+                    [ "rotate("
+                    , String.fromFloat rotation
+                    , ") translate(80)"
+                    ]
+                )
+            ]
+            []
+
+
+    line color rotation =
+        Svg.line
+            [ Svg.Attributes.strokeWidth "1"
+            , Svg.Attributes.x1 "0"
+            , Svg.Attributes.y1 "0"
+            , Svg.Attributes.x1 "80"
+            , Svg.Attributes.y1 "0"
+            , Svg.Attributes.stroke color
+            , Svg.Attributes.transform
+                (String.concat
+                    [ "rotate("
+                    , String.fromFloat rotation
+                    , ")"
+                    ]
+                )
+            ]
+            []
+
+Not much changed. Instead of passing two separate values for {Code|color} and {Code|rotation} we pass one value with two named fields. That's a record!
+
+Why did I do it? That way I can store complete information about a segment in one value. I need this for the *dictionary* of rules.
+
+Dictionary let's us associate one value (let's say color) with another value (let's say a list of segments). You can have as many entries in your dictionary as you want. That's perfect for storing our rules. We can start with an empty dictionary and then add our two rules: one for brown and one for green segments. We do it like this:
+
+| Code
+    module Main exposing (main)
+
+    import Dict
+    import Element
+    import Svg exposing (Svg)
+    import Svg.Attributes
+
+
+    main =
+        [ segment { color = "skyblue", rotation = 0 }
+            [ segment { color = "red", rotation = 15 } []
+            , segment { color = "blue", rotation = -15 }
+                [ segment { color = "yellow", rotation = 45 } []
+                , segment { color = "pink", rotation = -20 } []
+                , segment { color = "green", rotation = -90 } []
+                ]
+            ]
+        , segment { color = "orange", rotation = 72 } []
+        , segment { color = "red", rotation = 144 } []
+        , segment { color = "lime", rotation = 216 } []
+        , segment { color = "maroon", rotation = 288 } []
+        ]
+            |> Svg.svg
+                [ Svg.Attributes.height "100%"
+                , Svg.Attributes.width "100%"
+                , Svg.Attributes.style "background: none"
+                , Svg.Attributes.viewBox "-500 -500 1000 1000"
+                ]
+            |> Element.html
+            |> Element.layout
+                [ Element.width Element.fill
+                , Element.height Element.fill
+                ]
+
+
+    rules =
+        Dict.empty
+            |> Dict.insert "brown"
+                [ { color = "green", rotation = 0 }
+                , { color = "green", rotation = 20 }
+                , { color = "green", rotation = -30 }
+                ]
+            |> Dict.insert "green"
+                [ { color = "red", rotation = -45 }
+                , { color = "red", rotation = -5 }
+                , { color = "red", rotation = 50 }
+                ]
+
+
+    segment { color, rotation } children =
+        Svg.g []
+            [ Svg.g
+                [ Svg.Attributes.transform
+                    (String.concat
+                        [ "rotate("
+                        , String.fromFloat rotation
+                        , ") translate(80)"
+                        ]
+                    )
+                ]
+                children
+            , dot color rotation
+            , line color rotation
+            ]
+
+
+    dot color rotation =
+        Svg.circle
+            [ Svg.Attributes.r "10"
+            , Svg.Attributes.cx "0"
+            , Svg.Attributes.cy "0"
+            , Svg.Attributes.fill color
+            , Svg.Attributes.transform
+                (String.concat
+                    [ "rotate("
+                    , String.fromFloat rotation
+                    , ") translate(80)"
+                    ]
+                )
+            ]
+            []
+
+
+    line color rotation =
+        Svg.line
+            [ Svg.Attributes.strokeWidth "1"
+            , Svg.Attributes.x1 "0"
+            , Svg.Attributes.y1 "0"
+            , Svg.Attributes.x1 "80"
+            , Svg.Attributes.y1 "0"
+            , Svg.Attributes.stroke color
+            , Svg.Attributes.transform
+                (String.concat
+                    [ "rotate("
+                    , String.fromFloat rotation
+                    , ")"
+                    ]
+                )
+            ]
+            []
+
+We have added an import statement for {Code|Dict} module and created a dictionary with two entries: one for {Code|"brown"} and one for {Code|"green"}. The values associated with these keys are a list of segment records. The meaning of this is following:
+
+| List
+    - Whenever a brown segment is created, also create three green children
+    - Whenever a green segment is created, also create three red children
+
+There is no rule for red segments, so they will have no children. Now we will apply the rules. We have to change the definition of {Code|segment} function. It will no longer take the second argument - children will be added according to rules, not arbitrarily.
+
+Here it is:
+
+| Code
+    segment { color, rotation } =
+        Svg.g []
+            [ rules
+                |> Dict.get color
+                |> Maybe.withDefault []
+                |> List.map segment
+                |> Svg.g
+                    [ Svg.Attributes.transform
+                        (String.concat
+                            [ "rotate("
+                            , String.fromFloat rotation
+                            , ") translate(80)"
+                            ]
+                        )
+                    ]
+            , dot color rotation
+            , line color rotation
+            ]
+
+We also have to change the definition of {Code|main}. Segment no longer takes two arguments, and it's children are calculated according to rules, so we can just add one brown segment and rotate it upward (-90 degree)
+
+| Code
+    main =
+        [ segment { color = "brown", rotation = -90 } ]
+            |> Svg.svg
+                [ Svg.Attributes.height "100%"
+                , Svg.Attributes.width "100%"
+                , Svg.Attributes.style "background: none"
+                , Svg.Attributes.viewBox "-500 -500 1000 1000"
+                ]
+            |> Element.html
+            |> Element.layout
+                [ Element.width Element.fill
+                , Element.height Element.fill
+                ]
+
+The result should be exactly like we wanted it to be:
+
+| Monospace
+    TODO: Implement tree example
+
+    | Tree
+        | Axiom
+            color = brown
+            rotation = -90
+
+        | Rule
+            | Parent
+                color = brown
+
+            | Child
+              color = "green"
+              rotation = 0
+
+            | Child
+              color = "green"
+              rotation = 20
+
+            | Child
+              color = "green"
+              rotation = -30
+
+        | Rule
+            | Parent
+                color = green
+
+            | Child
+              color = "red"
+              rotation = -45
+
+            | Child
+              color = "red"
+              rotation = -5
+
+            | Child
+              color = "red"
+              rotation = 50
+
+Nice, but what's going on here:
+
+| Code
+    segment { color, rotation } =
+        Svg.g []
+            [ rules
+                |> Dict.get color
+                |> Maybe.withDefault []
+                |> List.map segment
+                |> Svg.g
+                    [ Svg.Attributes.transform
+                        (String.concat
+                            [ "rotate("
+                            , String.fromFloat rotation
+                            , ") translate(80)"
+                            ]
+                        )
+                    ]
+            , dot color rotation
+            , line color rotation
+            ]
+
+There are three new things here:
+
+| List
+    #. {Code|Dict.get}
+
+    #. {Code|Maybe.withDefault}
+
+    #. {Code|List.map}
+
+The first one is pretty simple. Previously we have been inserting entries into the dictionary. Each entry has a key (color of the segment) and value (list of child segments). Now we are getting these values back.
+
+But what if the entry for a given key is not there? For example we have not inserted the entry for the {Code|"red"} key? Well, then we get {Code|Nothing}. But our {Code|segment} function cannot work with nothing - it has to get a record with color and rotation. So when we have nothing, we can't call the {Code|segment} function. To understand this, consider the following.
+
+If there is an entry, we will get a list of records. We want a list of segments. Each of the records can be passed to the {Code|segment} function to produce one segment. That's where {Code|List.map} comes in. Given a list and a function it will call the function with each of the elements in the list and give back the list of produced values. Let's see a simpler example in the REPL:
+
+| Terminal
+    > fun something = something ++ " is fun!"
+    <function> : String -> String
+    > fun "Learning Elm"
+    "Learning Elm is fun!" : String
+    > fun "Tree"
+    "Tree is fun!" : String
+    > fun "Software"
+    "Software is fun!" : String
+    > things = [ "Elm", "Software", "Tree" ]
+    ["Elm","Software","Tree"] : List String
+    > List.map fun things
+    ["Elm is fun!","Software is fun!","Tree is fun!"] : List String
+    >
+
+Take some time to grasp it and then compare with our code.
+
+| Monospace
+    rules
+        |> Dict.get color
+        |> Maybe.withDefault []
+        |> List.map segment
+        |> Svg.g
+            [ Svg.Attributes.transform
+                (String.concat
+                    [ "rotate("
+                    , String.fromFloat rotation
+                    , ") translate(80)"
+                    ]
+                )
+            ]
+
+Let's analyze it line by line:
+
+| List
+    #. First line evaluates to a dictionary of rules.
+    #. Second takes that dictionary and gets a list of records for a given color (it maybe nothing if there is no entry, for example in case of {Code|"red"}).
+    #. Third gives an empty list if the result of second line was nothing.
+    #. Fourth maps this list of records with {Code|segment} function, producing a list of segments (the result can be an empty list if the input was empty)
+    #. Finally we pass the list to the {Code|Svg.g} function, producing a single group (again, possibly empty).
+
+It's a lot to absorb, so take your time to think about it.
+

Update day-2 to use new Dots block.

index 5c27c5f..d0a4ad1 100644
--- a/content/day-2.txt
+++ b/content/day-2.txt
@@ -26,34 +26,11 @@ Previously we have created a program that displays one dot in the center of the
=        radius = 80
=        scatter = False
=
-
-| Window
-    | Dots
-        | Dot
-            cx = 0
-            cy = 0
-            color = skyblue
-            radius = 10
-
-        | Dot
-            cx = -50
-            cy = 0
-            color = pink
-            radius = 10
-
-        | Dot
-            cx = 50
-            cy = 0
-            color = lime
-            radius = 10
-
=| Header
=    Multiple Dots
=
-| Note
-    See the FIXME comment in {Code|Main.elm} on {Code|wip-dots-example} branch.
=
-First obvious difference is that now we have multiple dots. The only dot we have is created using {Code|Svg.circle} function on line 9 - 15, this code block:
+First obvious difference is that now we have multiple dots. The only dot we have created using {Code|Svg.circle} function on line 9 - 15, this code block:
=
=| Monospace
=    Svg.circle
@@ -103,20 +80,24 @@ If you look closely in your source code, the block above is surrounded by {Code|
=
=Now the list spans from lines 9 to 23 and contains two items: skyblue and orange dots. The result should be:
=
-| Monospace
-    | Window
-        | Dots
-            | Dot
-                cx = 0
-                cy = 0
-                color = skyblue
-                radius = 10
-
-            | Dot
-                cx = 0
-                cy = 0
-                color = orange
-                radius = 10
+| Window
+    | Dots
+        | Container
+            background = none
+            fill = True
+            viewBox = -300 -300 600 600
+
+        | Dot
+            radius = 10
+            cx = 0
+            cy = 0
+            color = skyblue
+
+        | Dot
+            radius = 10
+            cx = 0
+            cy = 0
+            color = orange
=
=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 {Code|x} and {Code|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:
=
@@ -157,20 +138,24 @@ Oh oh! The list contains two dots, but we can only see the orange one on the scr
=
=Now you should see this:
=
-| Monospace
-    | Window
-        | Dots
-            | Dot
-                cx = 0
-                cy = 0
-                color = skyblue
-                radius = 10
-
-            | Dot
-                cx = 50
-                cy = 0
-                color = orange
-                radius = 10
+| Window
+    | Dots
+        | Container
+            background = none
+            fill = True
+            viewBox = -300 -300 600 600
+
+        | Dot
+            radius = 10
+            cx = 0
+            cy = 0
+            color = skyblue
+
+        | Dot
+            radius = 10
+            cx = 50
+            cy = 0
+            color = orange
=
=We can add more dots, by simply multiplying the code blocks inside the list. Each block must be separated from others with a {Code|,} (comma). Each time the {Code|cx} and {Code|cy} coordinates need to be different. We can also give dots different colors. Make five of them.
=
@@ -238,47 +223,41 @@ The {Code|r} attribute stands for radius. Then it was basically dictating the si
=        radius = 80
=
=| Row
-    | Monospace
-        TODO: Dots example
-        | Dots
-            | Container
-                viewBox = -100 -100 200 200
-                background = none
-                fill = True
-
-            | Dot
-                radius = 40
-                cx = 0
-                cy = 0
-                color = skyblue
+    | Dots
+        | Container
+            background = none
+            fill = True
+            viewBox = -100 -100 200 200
=
-    | Monospace
-        TODO: Dots example
-        | Dots
-            | Container
-                viewBox = -100 -100 200 200
-                background = none
-                fill = True
-
-            | Dot
-                radius = 60
-                cx = 0
-                cy = 0
-                color = skyblue
+        | Dot
+            radius = 40
+            cx = 0
+            cy = 0
+            color = skyblue
=
-    | Monospace
-        TODO: Dots example
-        | Dots
-            | Container
-                viewBox = -100 -100 200 200
-                background = none
-                fill = True
-
-            | Dot
-                radius = 80
-                cx = 0
-                cy = 0
-                color = skyblue
+    | Dots
+        | Container
+            background = none
+            fill = True
+            viewBox = -100 -100 200 200
+
+        | Dot
+            radius = 60
+            cx = 0
+            cy = 0
+            color = skyblue
+
+    | Dots
+        | Container
+            background = none
+            fill = True
+            viewBox = -100 -100 200 200
+
+        | Dot
+            radius = 80
+            cx = 0
+            cy = 0
+            color = skyblue
=
=This time the radius will affect the size of an imaginary circle on which the dots are laying.
=
@@ -387,8 +366,6 @@ In physical world we often use a tool called protractor to measure angles. It ty
=    radius = 400
=    strokeWidth = 2
=
-| Note
-    TODO: Modify the image to make marks 0 - 360. We can convert it to Elm code (using https:////level.app//svg-to-elm) and then reuse it in our examples.
=
=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?
=
@@ -558,7 +535,7 @@ For that we need to add another transformation before the move. First we will ro
=        []
=    ]
=
-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.
+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 it's internal coordinates system. So the translation will move it in a different direction then the first dot.
=
=Now it's time to add the third, red dot. Duplicate the second one, change fill to `"red"` and put `144` for rotate function, like this:
=

Merge remote-tracking branch 'origin/wip-dots-example' into content

Write section about halting on day 4

index e5d8848..9617799 100644
--- a/content/day-4.txt
+++ b/content/day-4.txt
@@ -652,6 +652,8 @@ The result should be exactly like we wanted it to be:
=        | Axiom
=            color = brown
=            rotation = -90
+            age = -1
+            growing = False
=
=        | Rule
=            | Parent
@@ -765,3 +767,176 @@ Let's analyze it line by line:
=
=It's a lot to absorb, so take your time to think about it.
=
+| Header
+    Towards Infinity
+
+Our tree is very cool but rather simple. Wouldn't it be nice if it could grow bigger and bigger? What if we could say that a brown segment produces two green (left and right) and a brown (straight)? Then the brown child would have two green and a brown too. But brown gives more brown /ad infinitum/ (pardon my latin).
+
+You can try that, but don't expect to see anything interesting in the browser. Your computer will trying very hard to draw a tree for you, but after going through hundreds of thousands of segments' generations it will give up, perhaps saying "too much recursion". According to the rules you gave there is no stopping to this tree.
+
+| Note
+    A funny fact about computers is that they generally can't tell if the task you give them can be finished or not. It's called {Link|the halting problem|url=https://en.wikipedia.org/wiki/Halting_problem}. Best they can do is give up after certain time. That's what happens here.
+
+So that won't work. But it would be nice to have arbitrarily complex trees with repeating patterns - that's how real trees grow. Since our problem is not ever stopping, maybe we can tell the program to simply stop after certain number of generations.
+
+We can do it like this. When we create our first segment (the brown one), we will pass it a second argument (let's call it {Code|age}). It will be an integer. The age of a parent will be decremented and passed to all it's children (so every child is younger than it's parent by one generation). Eventually the program will reach segments with {Code|age} being {Code|0}. This segment will have no children, thus stopping the whole process. Here is how we can implement it:
+
+| Code
+    module Main exposing (main)
+
+    import Dict
+    import Element
+    import Svg exposing (Svg)
+    import Svg.Attributes
+
+
+    main =
+        [ segment 4 { color = "brown", rotation = -90 } ]
+            |> Svg.svg
+                [ Svg.Attributes.height "100%"
+                , Svg.Attributes.width "100%"
+                , Svg.Attributes.style "background: none"
+                , Svg.Attributes.viewBox "-500 -500 1000 1000"
+                ]
+            |> Element.html
+            |> Element.layout
+                [ Element.width Element.fill
+                , Element.height Element.fill
+                ]
+
+
+    rules =
+        Dict.empty
+            |> Dict.insert "brown"
+                [ { color = "brown", rotation = 0 }
+                , { color = "green", rotation = 20 }
+                , { color = "green", rotation = -30 }
+                ]
+            |> Dict.insert "green"
+                [ { color = "red", rotation = -45 }
+                , { color = "red", rotation = -5 }
+                , { color = "red", rotation = 50 }
+                ]
+
+
+    segment age { color, rotation } =
+        if age == 0 then
+            Svg.g [] []
+
+        else
+            Svg.g []
+                [ rules
+                    |> Dict.get color
+                    |> Maybe.withDefault []
+                    |> List.map (segment (age - 1))
+                    |> Svg.g
+                        [ Svg.Attributes.transform
+                            (String.concat
+                                [ "rotate("
+                                , String.fromFloat rotation
+                                , ") translate(80)"
+                                ]
+                            )
+                        ]
+                , dot color rotation
+                , line color rotation
+                ]
+
+
+    dot color rotation =
+        Svg.circle
+            [ Svg.Attributes.r "10"
+            , Svg.Attributes.cx "0"
+            , Svg.Attributes.cy "0"
+            , Svg.Attributes.fill color
+            , Svg.Attributes.transform
+                (String.concat
+                    [ "rotate("
+                    , String.fromFloat rotation
+                    , ") translate(80)"
+                    ]
+                )
+            ]
+            []
+
+
+    line color rotation =
+        Svg.line
+            [ Svg.Attributes.strokeWidth "1"
+            , Svg.Attributes.x1 "0"
+            , Svg.Attributes.y1 "0"
+            , Svg.Attributes.x1 "80"
+            , Svg.Attributes.y1 "0"
+            , Svg.Attributes.stroke color
+            , Svg.Attributes.transform
+                (String.concat
+                    [ "rotate("
+                    , String.fromFloat rotation
+                    , ")"
+                    ]
+                )
+            ]
+            []
+
+On line 10 we pass the age to the {Code|segment} function as the first argument. The record is a second parameter  now. The reason is that it makes mapping on line 47 easier. Age is the same for all the children (age of the parent minus 1).
+
+On line 39 we see something new. It's the {Code|if ... then ... else ...} expression. You can probably guess how it works: first we give it a condition (in this case {Code|age == 0} - notice the double {Code|=}). That's our halting condition.
+
+In the {Code|then} block we put the value to produce if the condition is met (here an empty SVG group). This will effectively stop the growth of the tree - no more children from this segment.
+
+The {Code|else} block contains the value to be produced when the condition is not met. In our case it means that the segment is old enough to have children.
+
+The {Code|else} value is almost the same as the whole {Code|segment} function before the change. The only difference is that on line 47 we pass the {Code|age - 1} to the {Code|segment} function used in {Code|List.map}.
+
+The result should be like this:
+
+| Monospace
+    TODO: Implement tree example
+
+    | Tree
+        | Axiom
+            color = brown
+            rotation = -90
+            age = 4
+            growing = False
+
+        | Rule
+            | Parent
+                color = brown
+
+            | Child
+              color = "green"
+              rotation = 0
+
+            | Child
+              color = "green"
+              rotation = 20
+
+            | Child
+              color = "green"
+              rotation = -30
+
+        | Rule
+            | Parent
+                color = green
+
+            | Child
+              color = "red"
+              rotation = -45
+
+            | Child
+              color = "red"
+              rotation = -5
+
+            | Child
+              color = "red"
+              rotation = 50
+
+| Emphasize
+    Go ahead and play with the rules and age.
+
+| Emphasize
+    😺🎾
+
+| Emphasize
+    It's safe - the tree will always stop growing after a given number of generations.

Write section about segments growing on day 4

index 9617799..f731a33 100644
--- a/content/day-4.txt
+++ b/content/day-4.txt
@@ -940,3 +940,205 @@ The result should be like this:
=
=| Emphasize
=    It's safe - the tree will always stop growing after a given number of generations.
+
+| Header
+    Every Branch was Once a Twig
+
+I hope you had fun playing with the tree. You can make it really interesting and complicated now. But it doesn't look very much like a real, organic tree. One problem is that with a real tree old branches are thick and long, while young are thin and short.
+
+Since the size depends on the age, and the age is given for every segment, we can fix that relatively easy. Let's start by making the dots smaller or bigger depending on the age of the segment, like this:
+
+| Code
+    segment age { color, rotation } =
+        if age == 0 then
+            Svg.g [] []
+
+        else
+            Svg.g []
+                [ rules
+                    |> Dict.get color
+                    |> Maybe.withDefault []
+                    |> List.map (segment (age - 1))
+                    |> Svg.g
+                        [ Svg.Attributes.transform
+                            (String.concat
+                                [ "rotate("
+                                , String.fromFloat rotation
+                                , ") translate(80)"
+                                ]
+                            )
+                        ]
+                , dot age color rotation
+                , line color rotation
+                ]
+
+
+    dot age color rotation =
+        Svg.circle
+            [ Svg.Attributes.r (String.fromFloat age)
+            , Svg.Attributes.cx "0"
+            , Svg.Attributes.cy "0"
+            , Svg.Attributes.fill color
+            , Svg.Attributes.transform
+                (String.concat
+                    [ "rotate("
+                    , String.fromFloat rotation
+                    , ") translate(80)"
+                    ]
+                )
+            ]
+            []
+
+| Monospace
+    TODO: Describe the change to code
+
+Next let's pass the age to the {Code|line} function and use it for both the thickness {Code|strokWidth} and length {Code|x2}.
+
+If you reload now, the tree will look as if it exploded. It's funny. The reason is that we did not adjust the translation of the child groups nor the dots. It's always 80, even though the length of the lines is variable - see lines {Code|TODO: line of group translation} and {Code|TODO: line of dot translation}. Let's change it like that:
+
+| Code
+    module Main exposing (main)
+
+    import Dict
+    import Element
+    import Svg exposing (Svg)
+    import Svg.Attributes
+
+
+    main =
+        [ segment 4 { color = "brown", rotation = -90 } ]
+            |> Svg.svg
+                [ Svg.Attributes.height "100%"
+                , Svg.Attributes.width "100%"
+                , Svg.Attributes.style "background: none"
+                , Svg.Attributes.viewBox "-500 -500 1000 1000"
+                ]
+            |> Element.html
+            |> Element.layout
+                [ Element.width Element.fill
+                , Element.height Element.fill
+                ]
+
+
+    rules =
+        Dict.empty
+            |> Dict.insert "brown"
+                [ { color = "brown", rotation = 0 }
+                , { color = "green", rotation = 20 }
+                , { color = "green", rotation = -30 }
+                ]
+            |> Dict.insert "green"
+                [ { color = "red", rotation = -45 }
+                , { color = "red", rotation = -5 }
+                , { color = "red", rotation = 50 }
+                ]
+
+
+    segment age { color, rotation } =
+        if age == 0 then
+            Svg.g [] []
+
+        else
+            Svg.g []
+                [ rules
+                    |> Dict.get color
+                    |> Maybe.withDefault []
+                    |> List.map (segment (age - 1))
+                    |> Svg.g
+                        [ Svg.Attributes.transform
+                            (String.concat
+                                [ "rotate("
+                                , String.fromFloat rotation
+                                , ") translate("
+                                , String.fromFloat (age * 10)
+                                , ")"
+                                ]
+                            )
+                        ]
+                , dot age color rotation
+                , line age color rotation
+                ]
+
+
+    dot age color rotation =
+        Svg.circle
+            [ Svg.Attributes.r (String.fromFloat age)
+            , Svg.Attributes.cx "0"
+            , Svg.Attributes.cy "0"
+            , Svg.Attributes.fill color
+            , Svg.Attributes.transform
+                (String.concat
+                    [ "rotate("
+                    , String.fromFloat rotation
+                    , ") translate("
+                    , String.fromFloat (age * 10)
+                    , ")"
+                    ]
+                )
+            ]
+            []
+
+
+    line age color rotation =
+        Svg.line
+            [ Svg.Attributes.strokeWidth "1"
+            , Svg.Attributes.x1 "0"
+            , Svg.Attributes.y1 "0"
+            , Svg.Attributes.x1 (String.fromFloat (age * 10))
+            , Svg.Attributes.y1 "0"
+            , Svg.Attributes.stroke color
+            , Svg.Attributes.strokeWidth (String.fromFloat age)
+            , Svg.Attributes.transform
+                (String.concat
+                    [ "rotate("
+                    , String.fromFloat rotation
+                    , ")"
+                    ]
+                )
+            ]
+            []
+
+Now the tree should look correctly, like this:
+
+| Monospace
+    TODO: Implement tree example
+
+    | Tree
+        | Axiom
+            color = brown
+            rotation = -90
+            age = 4
+            growing = True
+
+        | Rule
+            | Parent
+                color = brown
+
+            | Child
+              color = "green"
+              rotation = 0
+
+            | Child
+              color = "green"
+              rotation = 20
+
+            | Child
+              color = "green"
+              rotation = -30
+
+        | Rule
+            | Parent
+                color = green
+
+            | Child
+              color = "red"
+              rotation = -45
+
+            | Child
+              color = "red"
+              rotation = -5
+
+            | Child
+              color = "red"
+              rotation = 50
+

Add congratulations block to day 4

index f731a33..98e45c9 100644
--- a/content/day-4.txt
+++ b/content/day-4.txt
@@ -1142,3 +1142,11 @@ Now the tree should look correctly, like this:
=              color = "red"
=              rotation = 50
=
+| Emphasize
+    Congratulations!
+
+| Emphasize
+    {Icon|name=award}
+
+| Emphasize
+    You are ready for {Link|the final day|url=/day-4.html}!

Fix mistakes on day 5 header

index e54b4b9..b7f1ebe 100644
--- a/content/day-5.txt
+++ b/content/day-5.txt
@@ -1,8 +1,14 @@
=| Title
-    Day 3
+    Day 5
=
=| Emphasize
-    Let's Make the Tree Grow
+    Let the Tree Grow
+
+
+| Note
+    *We are still working on this content.*
+
+    Stay tuned {Icon|name=radio}
=
=
=| Note
@@ -15,7 +21,4 @@
=| Header
=    The Problem
=
-| Note
-    *We are still working on this content.*
=
-    Stay tuned {Icon|name=radio}

Fix bug in halting condition of segment (day 4)

index 98e45c9..1816461 100644
--- a/content/day-4.txt
+++ b/content/day-4.txt
@@ -820,7 +820,7 @@ We can do it like this. When we create our first segment (the brown one), we wil
=
=
=    segment age { color, rotation } =
-        if age == 0 then
+        if age <= 0 then
=            Svg.g [] []
=
=        else
@@ -880,7 +880,7 @@ We can do it like this. When we create our first segment (the brown one), we wil
=
=On line 10 we pass the age to the {Code|segment} function as the first argument. The record is a second parameter  now. The reason is that it makes mapping on line 47 easier. Age is the same for all the children (age of the parent minus 1).
=
-On line 39 we see something new. It's the {Code|if ... then ... else ...} expression. You can probably guess how it works: first we give it a condition (in this case {Code|age == 0} - notice the double {Code|=}). That's our halting condition.
+On line 39 we see something new. It's the {Code|if ... then ... else ...} expression. You can probably guess how it works: first we give it a condition (in this case {Code|age <= 0} - meaning that age is zero or less). That's our halting condition.
=
=In the {Code|then} block we put the value to produce if the condition is met (here an empty SVG group). This will effectively stop the growth of the tree - no more children from this segment.
=
@@ -950,7 +950,7 @@ Since the size depends on the age, and the age is given for every segment, we ca
=
=| Code
=    segment age { color, rotation } =
-        if age == 0 then
+        if age <= 0 then
=            Svg.g [] []
=
=        else
@@ -1035,7 +1035,7 @@ If you reload now, the tree will look as if it exploded. It's funny. The reason
=
=
=    segment age { color, rotation } =
-        if age == 0 then
+        if age <= 0 then
=            Svg.g [] []
=
=        else

Correct link from day 4 to day 5

index 1816461..03acab1 100644
--- a/content/day-4.txt
+++ b/content/day-4.txt
@@ -1149,4 +1149,4 @@ Now the tree should look correctly, like this:
=    {Icon|name=award}
=
=| Emphasize
-    You are ready for {Link|the final day|url=/day-4.html}!
+    You are ready for {Link|the final day|url=/day-5.html}!

Draft day 5

index b7f1ebe..ce02674 100644
--- a/content/day-5.txt
+++ b/content/day-5.txt
@@ -21,4 +21,372 @@
=| Header
=    The Problem
=
+We have a nice picture of a tree, but ultimately it may have been easier to just draw it using some graphics program. We promised you a growing tree, so let's make it grow like this:
=
+| Monospace
+    TODO: Implement growing tree example
+
+    | AnimatedTree
+        | Axiom
+            color = brown
+            rotation = -90
+            minAge = 0
+            maxAge = 10
+
+        | Rule
+            | Parent
+                color = brown
+
+            | Child
+              color = "green"
+              rotation = 0
+
+            | Child
+              color = "green"
+              rotation = 20
+
+            | Child
+              color = "green"
+              rotation = -30
+
+        | Rule
+            | Parent
+                color = green
+
+            | Child
+              color = "red"
+              rotation = -45
+
+            | Child
+              color = "red"
+              rotation = -5
+
+            | Child
+              color = "red"
+              rotation = 50
+
+| Header
+    The Elm Architecture
+
+So far our programs were very static. The value of {Code|main} would be evaluated once, painted on the screen and stay there forever. Now we need to make our program react to the events in outside world. What events? A passage of time {Icon|name=watch} If we want our tree to grow over time, we need to know how much time have passed, so we can update it's age accordingly.
+
+In Elm we can do it using a different type of {Code|main}. One that ties together four functions:
+
+| List
+    #. {Code|init} specifying what's the initial state of the running program
+
+    #. {Code|subscriptions} specifying what events we are expecting and what messages will be sent when they happen
+
+    #. {Code|update} specifying how the state of the application will change when messages are received
+
+    #. {Code|view} specifying what value should be displayed depending on the current state.
+
+State is sometimes called model. There are also commands, but we will not use them in our program.
+
+| Image
+    src = /assets/Elm MVU architecture.jpg
+    description = "Model View Update - The Elm Architecture"
+
+| Monospace
+    TODO: Attribution
+
+First let's change the value of {Code|main} and create the {Code|view} function, like this:
+
+| Code
+    module Main exposing (main)
+
+    import Browser
+    import Dict
+    import Element
+    import Svg exposing (Svg)
+    import Svg.Attributes
+
+
+    main =
+        Browser.element
+            { init = init
+            , view = view
+            , update = update
+            , subscriptions = subscriptions
+            }
+
+
+    view age =
+        [ segment (age / 5000) { color = "brown", rotation = -90 } ]
+            |> Svg.svg
+                [ Svg.Attributes.height "100%"
+                , Svg.Attributes.width "100%"
+                , Svg.Attributes.style "background: none"
+                , Svg.Attributes.viewBox "-500 -500 1000 1000"
+                ]
+            |> Element.html
+            |> Element.layout
+                [ Element.width Element.fill
+                , Element.height Element.fill
+                ]
+
+
+    rules =
+        Dict.empty
+            |> Dict.insert "brown"
+                [ { color = "brown", rotation = 0 }
+                , { color = "green", rotation = 20 }
+                , { color = "green", rotation = -30 }
+                ]
+            |> Dict.insert "green"
+                [ { color = "red", rotation = -45 }
+                , { color = "red", rotation = -5 }
+                , { color = "red", rotation = 50 }
+                ]
+
+
+    segment age { color, rotation } =
+        if age <= 0 then
+            Svg.g [] []
+
+        else
+            Svg.g []
+                [ rules
+                    |> Dict.get color
+                    |> Maybe.withDefault []
+                    |> List.map (segment (age - 1))
+                    |> Svg.g
+                        [ Svg.Attributes.transform
+                            (String.concat
+                                [ "rotate("
+                                , String.fromFloat rotation
+                                , ") translate("
+                                , String.fromFloat (age * 10)
+                                , ")"
+                                ]
+                            )
+                        ]
+                , dot age color rotation
+                , line age color rotation
+                ]
+
+
+    dot age color rotation =
+        Svg.circle
+            [ Svg.Attributes.r (String.fromFloat age)
+            , Svg.Attributes.cx "0"
+            , Svg.Attributes.cy "0"
+            , Svg.Attributes.fill color
+            , Svg.Attributes.transform
+                (String.concat
+                    [ "rotate("
+                    , String.fromFloat rotation
+                    , ") translate("
+                    , String.fromFloat (age * 10)
+                    , ")"
+                    ]
+                )
+            ]
+            []
+
+
+    line age color rotation =
+        Svg.line
+            [ Svg.Attributes.strokeWidth "1"
+            , Svg.Attributes.x1 "0"
+            , Svg.Attributes.y1 "0"
+            , Svg.Attributes.x1 (String.fromFloat (age * 10))
+            , Svg.Attributes.y1 "0"
+            , Svg.Attributes.stroke color
+            , Svg.Attributes.strokeWidth (String.fromFloat age)
+            , Svg.Attributes.transform
+                (String.concat
+                    [ "rotate("
+                    , String.fromFloat rotation
+                    , ")"
+                    ]
+                )
+            ]
+            []
+
+Notice that the {Code|view} takes an argument called {Code|age} and passes it to the first (brown) segment. The value of {Code|age} is our state. In some programs the type of state can be very complex, but in our program it's simply a {Code|Float} number. It will indicate how much time in milliseconds have passed from the start of the program. Because the time is counted in milliseconds, to get correct results we need to divide it by 5000 (so the tree will grows one generation of segments per five second).
+
+Now let's provide the init function. It takes an argument sometimes called flags, but we will ignore it. Let's just say that it will always receive {Code|()} - an empty value called unit. It needs to return two values bound together in a structure called /tuple/.
+
+| Note
+    Tuple is like a list, but have a fixed number of elements. Unlike the lists, elements of tuples can have different types.
+
+The second value is a command. We don't need any, so we just give it a {Code|Cmd.none}, which as you can guess produces a command that does nothing.
+
+First value is more important. This is the initial age of the tree, right after the program starts. Since no time have passed yet, we set it to 0.
+
+That's our init:
+
+| Code
+    init () =
+        ( 0, Cmd.none )
+
+Time for the {Code|update} function. It will get two arguments: an incoming message and the current state. Incoming message will always be a duration of time since previous frame, so let's simply call it {Code|duration}. The state is the current age of the tree, so again we can call it {Code|age}. In return we must produce a tuple with:
+
+| List
+    #. the new state ({Code|age + duration})
+    #. a command (we don't need it, so we give {Code|Cmd.none})
+
+It looks like this:
+
+| Code
+    update duration age =
+        ( age + duration
+        , Cmd.none
+        )
+
+Finally let's subscribe to the events marking the passage of time. We can do it using {Code|Browser.Events.onAnimationFrameDelta}. Here is how it works. Whenever the browser is ready for the next frame it will send us a message containing the number of milliseconds that have passed since previous frame.
+
+To use it we first need to import the {Code|Browser.Events} module. The {Code|Browser.Events.onAnimationFrameDelta} function expects us to give it a function that will get a duration and return a message. But our message is simply a duration! We don't need to do anything with it, just take it as it is. So we need a function that just returns whatever it gets. This function is called {Code|identity}.
+
+| Note
+    Perhaps you have noticed that we use a lot of functions. Functions are passed to functions that sometimes return functions 🤕 That's why Elm is called a *functional programming language*.
+
+The whole {Code|subscriptions} looks like that:
+
+| Code
+    subscriptions age =
+        Browser.Events.onAnimationFrameDelta identity
+
+And the complete code like this:
+
+| Code
+    module Main exposing (main)
+
+    import Browser
+    import Browser.Events
+    import Dict
+    import Element
+    import Svg exposing (Svg)
+    import Svg.Attributes
+
+
+    main =
+        Browser.element
+            { init = init
+            , view = view
+            , update = update
+            , subscriptions = subscriptions
+            }
+
+
+    init () =
+        ( 0, Cmd.none )
+
+
+    view age =
+        [ segment (age / 5000) { color = "brown", rotation = -90 } ]
+            |> Svg.svg
+                [ Svg.Attributes.height "100%"
+                , Svg.Attributes.width "100%"
+                , Svg.Attributes.style "background: none"
+                , Svg.Attributes.viewBox "-500 -500 1000 1000"
+                ]
+            |> Element.html
+            |> Element.layout
+                [ Element.width Element.fill
+                , Element.height Element.fill
+                ]
+
+
+    update duration age =
+        ( duration
+            |> Debug.log "Duration"
+            |> (+) age
+            |> Debug.log "Age"
+        , Cmd.none
+        )
+
+
+    subscriptions age =
+        Browser.Events.onAnimationFrameDelta identity
+
+
+    rules =
+        Dict.empty
+            |> Dict.insert "brown"
+                [ { color = "brown", rotation = 0 }
+                , { color = "green", rotation = 20 }
+                , { color = "green", rotation = -30 }
+                ]
+            |> Dict.insert "green"
+                [ { color = "red", rotation = -45 }
+                , { color = "red", rotation = -5 }
+                , { color = "red", rotation = 50 }
+                ]
+
+
+    segment age { color, rotation } =
+        if age <= 0 then
+            Svg.g [] []
+
+        else
+            Svg.g []
+                [ rules
+                    |> Dict.get color
+                    |> Maybe.withDefault []
+                    |> List.map (segment (age - 1))
+                    |> Svg.g
+                        [ Svg.Attributes.transform
+                            (String.concat
+                                [ "rotate("
+                                , String.fromFloat rotation
+                                , ") translate("
+                                , String.fromFloat (age * 10)
+                                , ")"
+                                ]
+                            )
+                        ]
+                , dot age color rotation
+                , line age color rotation
+                ]
+
+
+    dot age color rotation =
+        Svg.circle
+            [ Svg.Attributes.r (String.fromFloat age)
+            , Svg.Attributes.cx "0"
+            , Svg.Attributes.cy "0"
+            , Svg.Attributes.fill color
+            , Svg.Attributes.transform
+                (String.concat
+                    [ "rotate("
+                    , String.fromFloat rotation
+                    , ") translate("
+                    , String.fromFloat (age * 10)
+                    , ")"
+                    ]
+                )
+            ]
+            []
+
+
+    line age color rotation =
+        Svg.line
+            [ Svg.Attributes.strokeWidth "1"
+            , Svg.Attributes.x1 "0"
+            , Svg.Attributes.y1 "0"
+            , Svg.Attributes.x1 (String.fromFloat (age * 10))
+            , Svg.Attributes.y1 "0"
+            , Svg.Attributes.stroke color
+            , Svg.Attributes.strokeWidth (String.fromFloat age)
+            , Svg.Attributes.transform
+                (String.concat
+                    [ "rotate("
+                    , String.fromFloat rotation
+                    , ")"
+                    ]
+                )
+            ]
+            []
+
+| Emphasize
+    That's it!
+
+| Emphasize
+    {Icon|name=flag}
+
+| Emphasize
+    Your tree is growing over time!
+
+We hope you had good time learning programming with us. If we still have some time left, we can play with the code.