Week 02 of 2019
Development log of Elm Tree Workshop
24 items
- Correct typo
- Make CartesianCoordinates example parametrized
- Small changes to Terminal and Code blocks
- Make Icon size dependant on the font size of the parent
- Re-draft day 1
- Temporary fix for #9
- Use external stylesheet to load Google fonts to avoid FOAC
- Upgrade feathericons/elm-feather to 1.3.0
- Correct the vertical alignment of Key, Icon and inline Code
- Make sure the title always displays Software Garden
- WIP: Create a general purpose Dots example
- Create Protractor example and display protractor in Circle example
- Make Dots example work, but without configurable continer
- Update markup to work with previously commit changes
- Remove two dots example
- Merge branch 'fix-9-replace-p-with-div' into 'master'
- Merge branch 'protractor' into 'master'
- Merge remote-tracking branch 'origin/master' into content
- Merge remote-tracking branch 'origin/master' into content
- Deploy
- Draft transition from one to five dots on day 2
- Make the Circle example take a radius parameter
- Edits to day 3
- Draft the rest of the syntax section
Correct typo
On by
index 6dbdda5..4c3c26d 100644
--- a/content/preparation.txt
+++ b/content/preparation.txt
@@ -34,7 +34,7 @@ and follow the installation instructions.
=
=Some of the tools we use with Elm require Node.js. Go to the {Link|Node.js|url=https://nodejs.org/en/} website and install the current version (the green button on the right)
=
-We will need to use the {Definition|term=terminal|definiens=a program that let's you enter commands for your computer} {Icon|name=terminal} a little bit. Open Lunchpad (the icon with a rocket) and type {Code|terminal}, like this:
+We will need to use the {Definition|term=terminal|definiens=a program that let's you enter commands for your computer} {Icon|name=terminal} a little bit. Open Launchpad (the icon with a rocket) and type {Code|terminal}, like this:
=
=| Image
= src = /assets/mac-launchpad-terminal.pngMake CartesianCoordinates example parametrized
On by
This required changes to CartesianPlane (graph and axes).
Also refactor code for other examples to use pipes and limit the width of layout (in some cases).
index 5fce82e..91b6b4e 100644
--- a/content/day-1.txt
+++ b/content/day-1.txt
@@ -254,6 +254,12 @@ Reload the browser to confirm your hypothesis. Voila! The SVG space now fills th
= cy=0
=
=It should now be possible to place our dot in the center of the screen. But, how exactly will we achieve it?
+| Row
+ | CartesianCoordinates
+ minX = -10
+ minY = -10
+ maxX = 300
+ maxY = 200
=
=Let's return to the idea we introduced earlier, that the center of the dot is at a point called the {Code|origin} or {Code|(0, 0)}. What exactly does this mean?
=
@@ -261,7 +267,6 @@ Let's return to the idea we introduced earlier, that the center of the dot is at
=
=Adjust the sliders below to change the x and y coordinates of the dot. It's like moving an object on a table by describing how far left, right, up or down it should be.
=
-| CartesianCoordinates
=
=Knowing that the center of the dot is at the {Code|origin} or point {Code|(0,0)} of our plain, we can see that by default the origin of an SVG space is at the upper left corner. If we want to help our dot get out of hiding, we'll have to place the center of the dot at a point with positive {Code|x} and {Code|y} values.
=index 6029a39..a162a84 100644
--- a/src/CartesianPlane.elm
+++ b/src/CartesianPlane.elm
@@ -11,24 +11,39 @@ import Triangle2d
=import Vector2d
=
=
-graph : Float -> List (Svg msg) -> Html msg
-graph size shapes =
+type alias Config =
+ { minX : Float
+ , minY : Float
+ , maxX : Float
+ , maxY : Float
+ }
+
+
+graph : Config -> List (Svg msg) -> Html msg
+graph config shapes =
= let
+ size =
+ Basics.max (config.maxX - config.minX) (config.maxY - config.minY)
+
= hairline =
- size / 1000
+ 20 / size
=
= fontsize =
- size / 200
+ 100 / size
=
= background =
= axes
= [ stroke "gray"
= , fill "gray"
= ]
- size
+ config
= in
= svg
- [ [ negate size / 2, negate size / 2, size, size ]
+ [ [ config.minX
+ , config.minY
+ , config.maxX - config.minX
+ , config.maxY - config.minY
+ ]
= |> List.map String.fromFloat
= |> String.join " "
= |> viewBox
@@ -40,22 +55,17 @@ graph size shapes =
= (background :: shapes)
=
=
-axes attributes size =
+axes : List (Attribute msg) -> Config -> Svg msg
+axes attributes config =
= let
- max =
- size / 2
-
- min =
- negate max
-
= xAxis =
- { start = Point2d.fromCoordinates ( min, 0 )
- , end = Point2d.fromCoordinates ( max, 0 )
+ { start = Point2d.fromCoordinates ( config.minX, 0 )
+ , end = Point2d.fromCoordinates ( config.maxX, 0 )
= }
=
= yAxis =
- { start = Point2d.fromCoordinates ( 0, min )
- , end = Point2d.fromCoordinates ( 0, max )
+ { start = Point2d.fromCoordinates ( 0, config.minY )
+ , end = Point2d.fromCoordinates ( 0, config.maxY )
= }
= in
= g []
@@ -67,9 +77,21 @@ axes attributes size =
= attributes
= yAxis.start
= yAxis.end
- , label [ fontSize "8", color "gray" ] (xAxis.end |> Point2d.translateIn (Direction2d.fromAngle (degrees -135)) 10) "x"
- , label [ fontSize "8", color "gray" ] (yAxis.end |> Point2d.translateIn (Direction2d.fromAngle (degrees -45)) 10) "y"
- , label [ fontSize "8", color "gray" ] (Point2d.origin |> Point2d.translateIn (Direction2d.fromAngle (degrees 135)) 10) "O"
+ , label [ fontSize "8", color "gray" ]
+ (xAxis.end
+ |> Point2d.translateIn (Direction2d.fromAngle (degrees -135)) 10
+ )
+ "x"
+ , label [ fontSize "8", color "gray" ]
+ (yAxis.end
+ |> Point2d.translateIn (Direction2d.fromAngle (degrees -45)) 10
+ )
+ "y"
+ , label [ fontSize "8", color "gray" ]
+ (Point2d.origin
+ |> Point2d.translateIn (Direction2d.fromAngle (degrees 135)) 10
+ )
+ "O"
= ]
=
=index 1c4266c..b8b1723 100644
--- a/src/Examples/CartesianCoordinates.elm
+++ b/src/Examples/CartesianCoordinates.elm
@@ -1,5 +1,6 @@
=module Examples.CartesianCoordinates exposing
- ( Model
+ ( Config
+ , Model
= , Msg
= , init
= , main
@@ -19,6 +20,23 @@ import Svg exposing (..)
=import Svg.Attributes exposing (..)
=
=
+type alias Config =
+ { minX : Float
+ , minY : Float
+ , maxX : Float
+ , maxY : Float
+ }
+
+
+defaults : Config
+defaults =
+ { minX = -150
+ , minY = -150
+ , maxX = 150
+ , maxY = 150
+ }
+
+
=main : Program () Model Msg
=main =
= Browser.sandbox
@@ -46,11 +64,16 @@ init =
=
=view : Model -> Html Msg
=view model =
- Element.layout
- [ Element.height Element.fill
- , Element.width Element.fill
- ]
- (ui model)
+ model
+ |> ui defaults
+ |> Element.el
+ [ Element.width (Element.maximum 600 Element.fill)
+ , Element.centerX
+ ]
+ |> Element.layout
+ [ Element.height Element.fill
+ , Element.width Element.fill
+ ]
=
=
=update : Msg -> Model -> Model
@@ -63,42 +86,48 @@ update msg model =
= { model | y = y }
=
=
-ui : Model -> Element Msg
-ui model =
+ui : Config -> Model -> Element Msg
+ui config model =
+ let
+ graph =
+ [ circle
+ [ cx <| String.fromFloat model.x
+ , cy <| String.fromFloat model.y
+ , r "2"
+ , fill "magenta"
+ ]
+ []
+ , text_
+ [ x <| String.fromFloat (model.x + 5)
+ , y <| String.fromFloat (model.y + 5)
+ , fontSize "6"
+ , fontFamily "Source Code Pro, monospace"
+ , fill "gray"
+ , dominantBaseline "central"
+ ]
+ [ text coordinates ]
+ ]
+ |> CartesianPlane.graph config
+ |> Element.html
+ |> Element.el
+ [ Element.height Element.fill
+ , Element.width Element.fill
+ ]
+
+ coordinates =
+ "{ x = "
+ ++ String.fromFloat model.x
+ ++ ", y = "
+ ++ String.fromFloat model.y
+ ++ "}"
+ in
= Element.column
= [ Element.width Element.fill
= , Element.centerX
= , Element.spacing 30
= , Element.padding 30
= ]
- [ Element.el
- [ Element.height Element.fill
- , Element.width Element.fill
- ]
- <|
- Element.html <|
- CartesianPlane.graph 300
- [ circle
- [ cx <| String.fromFloat model.x
- , cy <| String.fromFloat model.y
- , r "2"
- , fill "magenta"
- ]
- []
- , text_
- [ x <| String.fromFloat (model.x + 0.03)
- , y <| String.fromFloat model.y
- , fontSize "0.05"
- , dominantBaseline "central"
- ]
- [ text <|
- "("
- ++ String.fromFloat model.x
- ++ ", "
- ++ String.fromFloat model.y
- ++ ")"
- ]
- ]
+ [ graph
= , Input.slider
= [ Element.behindContent
= (Element.el
@@ -114,9 +143,9 @@ ui model =
= { onChange = SetX
= , label =
= Input.labelBelow [ Element.centerX ] <|
- Element.text ("x value: " ++ String.fromFloat model.x)
- , min = -150
- , max = 150
+ Element.text ("x = " ++ String.fromFloat model.x)
+ , min = config.minX
+ , max = config.maxX
= , value = model.x
= , thumb = Input.defaultThumb
= , step = Just 0.01
@@ -136,9 +165,9 @@ ui model =
= { onChange = SetY
= , label =
= Input.labelBelow [ Element.centerX ] <|
- Element.text ("y value: " ++ String.fromFloat model.y)
- , min = -150
- , max = 150
+ Element.text ("y = " ++ String.fromFloat model.y)
+ , min = config.minY
+ , max = config.maxY
= , value = model.y
= , thumb = Input.defaultThumb
= , step = Just 0.01index 35baf6c..a79414c 100644
--- a/src/Examples/Circle.elm
+++ b/src/Examples/Circle.elm
@@ -33,11 +33,15 @@ type alias Config =
=
=main : Html.Html msg
=main =
- Element.layout
- [ Element.width Element.fill
- , Element.height Element.fill
- ]
- (ui defaults)
+ ui defaults
+ |> Element.el
+ [ Element.width (Element.maximum 600 Element.fill)
+ , Element.centerX
+ ]
+ |> Element.layout
+ [ Element.width Element.fill
+ , Element.height Element.fill
+ ]
=
=
=palette : List Stringindex 46899d3..11dbad5 100644
--- a/src/Examples/LineTypedTransformations.elm
+++ b/src/Examples/LineTypedTransformations.elm
@@ -34,10 +34,6 @@ type Transformation
= | Rotate Float
=
=
-
--- transform : List Transformation -> Svg.Attribute msg
-
-
=transform transformations =
= let
= toString : Transformation -> Stringindex b1bc6f5..f4a9dbf 100644
--- a/src/Examples/NestedTransformations.elm
+++ b/src/Examples/NestedTransformations.elm
@@ -154,7 +154,12 @@ ui model =
= in
= shape
= |> List.singleton
- |> CartesianPlane.graph 300
+ |> CartesianPlane.graph
+ { minX = -150
+ , minY = -150
+ , maxX = 150
+ , maxY = 150
+ }
= |> wrapper
=
=
@@ -441,5 +446,12 @@ grid attributes unit size =
= |> List.map LineSegment2d.fromEndpoints
= |> List.map (Geometry.Svg.lineSegment2d attributes)
= )
- |> (::) (CartesianPlane.axes attributes (size * unit))
+ |> (::)
+ (CartesianPlane.axes attributes
+ { minX = size * unit * -0.5
+ , minY = size * unit * -0.5
+ , maxX = size * unit * 0.5
+ , maxY = size * unit * 0.5
+ }
+ )
= |> g []index 4fd1e93..8f552f0 100644
--- a/src/Examples/PolarCoordinates.elm
+++ b/src/Examples/PolarCoordinates.elm
@@ -70,7 +70,12 @@ ui model =
= ]
= <|
= Element.html <|
- CartesianPlane.graph 300
+ CartesianPlane.graph
+ { minX = -150
+ , minY = -150
+ , maxX = 150
+ , maxY = 150
+ }
= [ circle
= [ cx <| String.fromFloat point.x
= , cy <| String.fromFloat point.yindex 3321826..15d42cf 100644
--- a/src/Examples/Transformations.elm
+++ b/src/Examples/Transformations.elm
@@ -57,11 +57,16 @@ init =
=
=view : Model -> Html Msg
=view model =
- Element.layout
- [ Element.height Element.fill
- , Element.width Element.fill
- ]
- (ui model)
+ model
+ |> ui
+ |> Element.el
+ [ Element.width (Element.maximum 600 Element.fill)
+ , Element.centerX
+ ]
+ |> Element.layout
+ [ Element.height Element.fill
+ , Element.width Element.fill
+ ]
=
=
=ui : Model -> Element Msg
@@ -72,8 +77,7 @@ ui model =
=
= wrapper element =
= Element.column
- [ Element.width (Element.maximum 600 Element.fill)
- , Element.centerX
+ [ Element.width Element.fill
= , Element.spacing 20
= ]
= [ Element.el
@@ -113,7 +117,12 @@ ui model =
= in
= shape
= |> List.singleton
- |> CartesianPlane.graph 300
+ |> CartesianPlane.graph
+ { minX = -150
+ , minY = -150
+ , maxX = 150
+ , maxY = 150
+ }
= |> wrapper
=
=
@@ -339,5 +348,12 @@ grid attributes unit size =
= |> List.map LineSegment2d.fromEndpoints
= |> List.map (Geometry.Svg.lineSegment2d attributes)
= )
- |> (::) (CartesianPlane.axes attributes (size * unit))
+ |> (::)
+ (CartesianPlane.axes attributes
+ { minX = -150
+ , minY = -150
+ , maxX = 150
+ , maxY = 150
+ }
+ )
= |> g []index 222da7a..576f5e8 100644
--- a/src/Examples/ViewBox.elm
+++ b/src/Examples/ViewBox.elm
@@ -47,8 +47,8 @@ init : Model
=init =
= { left = 0
= , top = 0
- , width = 400
- , height = 400
+ , width = 50
+ , height = 50
= , preserveAspectRatio = False
= }
=
@@ -95,7 +95,7 @@ ui model =
= let
= viewbox =
= svg
- [ viewBox "-500 -500 1000 1000"
+ [ viewBox "-100 -100 200 200"
= ]
= [ g [] world
= , rect
@@ -187,8 +187,8 @@ ui model =
= , label =
= Input.labelBelow [ Element.centerX ] <|
= Element.text ("left: " ++ String.fromFloat model.left)
- , min = -500
- , max = 500
+ , min = -100
+ , max = 100
= , value = model.left
= , thumb = Input.defaultThumb
= , step = Just 1
@@ -209,8 +209,8 @@ ui model =
= , label =
= Input.labelBelow [ Element.centerX ] <|
= Element.text ("top: " ++ String.fromFloat model.top)
- , min = -500
- , max = 500
+ , min = -100
+ , max = 100
= , value = model.top
= , thumb = Input.defaultThumb
= , step = Just 1
@@ -232,7 +232,7 @@ ui model =
= Input.labelBelow [ Element.centerX ] <|
= Element.text ("width: " ++ String.fromFloat model.width)
= , min = 1
- , max = 500
+ , max = 100
= , value = model.width
= , thumb = Input.defaultThumb
= , step = Just 1
@@ -254,7 +254,7 @@ ui model =
= Input.labelBelow [ Element.centerX ] <|
= Element.text ("height: " ++ String.fromFloat model.height)
= , min = 1
- , max = 500
+ , max = 100
= , value = model.height
= , thumb = Input.defaultThumb
= , step = Just 1
@@ -270,7 +270,20 @@ ui model =
=
=world : List (Svg Msg)
=world =
- [ circle [ cx "400", cy "300", r "250", fill "purple" ] []
- , circle [ cx "200", cy "350", r "50", fill "yellow" ] []
- , circle [ cx "0", cy "0", r "20", fill "red" ] []
+ [ CartesianPlane.axes
+ [ stroke "gray"
+ , fill "gray"
+ ]
+ { minX = -100
+ , minY = -100
+ , maxX = 100
+ , maxY = 100
+ }
+ , circle
+ [ cx "0"
+ , cy "0"
+ , r "2"
+ , fill "magenta"
+ ]
+ []
= ]index 9a9da15..782b5a7 100644
--- a/src/Main.elm
+++ b/src/Main.elm
@@ -209,10 +209,10 @@ view model =
= "Expecting a block name " ++ name
=
= Mark.ExpectingInlineName name ->
- "Expecting an inline name" ++ name
+ "Expecting an inline name " ++ name
=
= Mark.ExpectingFieldName name ->
- "Expecting a field name" ++ name
+ "Expecting a field name " ++ name
=
= Mark.NonMatchingFields { expecting, found } ->
= "Fields don't match. Expecting one of [ "
@@ -640,13 +640,23 @@ document =
= cartesianCoordinates : Mark.Block (Examples.Model -> Element Msg)
= cartesianCoordinates =
= let
- render model =
+ render :
+ Examples.CartesianCoordinates.Config
+ -> Examples.Model
+ -> Element Msg
+ render config model =
= model.cartesianCoordinates
- |> Examples.CartesianCoordinates.ui
+ |> Examples.CartesianCoordinates.ui config
= |> Element.map Examples.CartesianCoordinatesMsg
= |> Element.map ExamplesMsg
= in
- Mark.stub "CartesianCoordinates" render
+ Mark.record4 "CartesianCoordinates"
+ Examples.CartesianCoordinates.Config
+ (Mark.field "minX" Mark.float)
+ (Mark.field "minY" Mark.float)
+ (Mark.field "maxX" Mark.float)
+ (Mark.field "maxY" Mark.float)
+ |> Mark.map render
=
= polarCoordinates : Mark.Block (Examples.Model -> Element Msg)
= polarCoordinates =Small changes to Terminal and Code blocks
On by
Font size, icons, fix background color not bleeding to the edge in Chromium.
index 73c5f76..c047718 100644
--- a/src/Mark/Custom.elm
+++ b/src/Mark/Custom.elm
@@ -105,7 +105,7 @@ code =
= , Background.color colors.charcoal
= , Font.color colors.gray
= ]
- [ FeatherIcons.file
+ [ FeatherIcons.fileText
= |> FeatherIcons.toHtml []
= |> Element.html
= |> Element.el
@@ -184,6 +184,7 @@ terminal =
= [ Border.width 3
= , Border.rounded 5
= , Border.color colors.charcoal
+ , Background.color colors.charcoal
= , css "page-break-inside" "avoid"
= , Font.family
= [ sourceCodePro
@@ -193,7 +194,6 @@ terminal =
= ]
= [ Element.row
= [ Element.width Element.fill
- , Background.color colors.charcoal
= , Font.color colors.gray
= ]
= [ FeatherIcons.terminal
@@ -226,7 +226,7 @@ terminal =
= , bottomLeft = 3
= , bottomRight = 3
= }
- , Font.size 18
+ , Font.size 14
= , Font.family
= [ sourceCodePro
= , Font.monospaceMake Icon size dependant on the font size of the parent
On by
Use em units instead of px.
index c047718..0a42947 100644
--- a/src/Mark/Custom.elm
+++ b/src/Mark/Custom.elm
@@ -396,8 +396,8 @@ icon =
= |> Dict.get name
= |> Maybe.map
= (FeatherIcons.toHtml
- [ Html.Attributes.height 14
- , Html.Attributes.width 14
+ [ Html.Attributes.style "height" "1em"
+ , Html.Attributes.style "width" "1em"
= ]
= )
= |> Maybe.map Element.htmlRe-draft day 1
On by
Back to single dot (it's hard enough for one day).
Move all semantics discussion to day 3 (not redacted yet).
index 91b6b4e..d1fc7a3 100644
--- a/content/day-1.txt
+++ b/content/day-1.txt
@@ -2,7 +2,7 @@
= Day 1
=
=| Emphasize
- Let's Make Some Dots!
+ Let's Make a Dot!
=
=
=| Note
@@ -22,7 +22,7 @@
=As mentioned before, programs are represented as text (called the /source code/). The source code is stored in files {Icon|name=file} and files are organized in directories {Icon|name=folder}.
=
=
-So the first step is to create a directory for our new program. Let's call it "software-garden".
+So the first step is to create a directory for our new program. Let's call it {Code|software-garden}.
=
=In the terminal type:
=
@@ -50,12 +50,34 @@ We can easily create a new program by entering a following command:
=| Terminal
= elm init
=
-Then to create the file which will contain our source code, type:
+You should see something similar to this:
+
+| Terminal
+ Hello! Elm projects always start with an elm.json file. I can create them!
+
+ Now you may be wondering, what will be in this file? How do I add Elm files to
+ my project? How do I see it in the browser? How will my code grow? Do I need
+ more directories? What about tests? Etc.
+
+ Check out <https://elm-lang.org/0.19.0/init> for all the answers!
+
+ Knowing all that, would you like me to create an elm.json file now? [Y/n]:
+
+
+Just press {Key|enter} once again to proceed. Then to create the file which will contain our source code, type:
=
=| Terminal
= atom src/Main.elm
=
-It may take few seconds, but eventually this command should open a new Atom editor window with an empty text file. If you prefer to use different editor, feel free to do so. Once the editor is ready, type the code belowand save the file.
+| Note
+ Notice that the word {Code|Main} is capitalized. It's important! You can enter uppercase letter {Code|M} with {Key|shift} + {Key|m}, which means:
+
+ | List
+ - Press and hold {Key|shift}
+ - While holding press the {Key|m} button once.
+ - Finally release the {Key|shift}.
+
+It may take few seconds, but eventually this command should open a new Atom editor window with an empty text file. If you prefer to use different editor, feel free to do so. Once the editor is ready, type the code below and save the file.
=
=| Code
= module Main exposing (main)
@@ -66,7 +88,7 @@ It may take few seconds, but eventually this command should open a new Atom edit
= main =
= Html.text "Hello, Tree"
=
-Then enter the following command in the terminal:
+Then switch back to the terminal and enter the following command:
=
=| Terminal
= elm reactor
@@ -135,8 +157,17 @@ We are going to use a technology called *SVG (Scalable Vector Graphics)*. It's a
= - While holding press the {Key|c} button once.
= - Finally release the {Key|ctrl}.
=
+In response you should see something like this:
=
-Now that we have {Code|elm//svg} installed, we can write the code that will draw a dot for us. Change the code to look like this:
+| Terminal
+ Here is my plan:
+
+ Add:
+ elm/svg 1.0.1
+
+ Would you like me to update your elm.json accordingly? [Y/n]:
+
+Press {Key|enter} to proceed with the installation. Now that we have {Code|elm//svg} installed, we can write the code that will draw a dot for us. Change the code to look like this:
=
=| Code
= module Main exposing (main)
@@ -168,12 +199,9 @@ Reload the browser. You should see something like this:
=
=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.
=
-| Note
- TODO: Picture // example showing the viewport
-
=It's a good start, but the dot is not in the center of the screen yet. Let's try to understand why it is where it is.
=
-First we have to realize that the dot is inside an SVG element that itself has a position, width and height. We can make the background of the SVG element pink to see where it is:
+First we have to realize that the dot is inside an {Code|svg} element that itself has a position, width and height. We can make the background of the {Code|svg} element pink to see where it is:
=
=| Code
= module Main exposing (main)
@@ -189,33 +217,21 @@ First we have to realize that the dot is inside an SVG element that itself has a
= []
= ]
=
-Realoading the browser should reveal something like this:
+Reloading the browser should reveal something like this:
=
=| Window
= | Dot
= background=pink
= fill=black
=
-Since the dot is inside the SVG element, and the 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 SVG fill entire {Definition|term=viewport|definiens=the area of the browser window where the content is displayed}.
-
-There are two reasons. First, because the center of the dot is at a point called the {Code|origin} or {Code|(0, 0)}. We'll explore exactly what this means later.
-
-Second, it's because the SVG space doesn't fill the browser window.
-
-
-When you reload the browser, you'll see this:
+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}.
=
+To do this we will use a package called {Code|mdgriffith//elm-ui}. It makes laying out elements relatively easy. Install it with the terminal. Press {Key|ctrl} + {Key|c} to stop the elm reactor and enter the command
=
+| Terminal
+ elm install mdgriffith/elm-ui
=
-Now we can clearly see that our SVG element (which is pink) does not fill the screen.
-
-We can easily correct this with the help of a very handy elm package, {Code|mdgriffith//elm-ui}.
-
-Install it with the terminal.
-
-Press {Key|ctrl} + {Key|c} to stop the elm-reactor, type {Code|elm install mdgriffith//elm-ui} and press {Key|enter}.
-
-Then in {Code|src//Main.elm} add the necessary import {Code|import Element} and change {Code|Main} to look like this:
+As before, press {Key|enter} to follow the plan. Now let's use the package. Change the code to look like this:
=
=| Code
= module Main exposing (main)
@@ -226,25 +242,33 @@ Then in {Code|src//Main.elm} add the necessary import {Code|import Element} and
=
=
= main =
- Element.layout
- [ Element.width Element.fill
- , Element.height Element.fill
+ Svg.svg
+ [ Svg.Attributes.style "background: pink"
+ , Svg.Attributes.height "100%"
+ , Svg.Attributes.width "100%"
= ]
- (Element.html
- (Svg.svg
- [ Svg.Attributes.height "100%"
- , Svg.Attributes.width "100%"
- , Svg.Attributes.style "background: pink"
- ]
- [ Svg.circle
- [ Svg.Attributes.r "10" ] []
- ]
- )
- )
-
-Take a wild guess: what do you think {Code|Element.height Element.fill}, {Code|Svg.Attributes.height "100%"}, {Code|Element.width Element.fill} and {Code|Svg.Attributes.width "100%"} do?
-
-Reload the browser to confirm your hypothesis. Voila! The SVG space now fills the screen.
+ [ Svg.circle
+ [ Svg.Attributes.r "10"
+ ]
+ []
+ ]
+ |> Element.html
+ |> Element.layout
+ [ Element.width Element.fill
+ , Element.height Element.fill
+ ]
+
+
+| Note
+ Don't worry if it all looks like black magic now. We will discuss what it all means on {Link|Day 3|url=day-3.html} of the workshop.
+
+ For now let's just note that on line 3 we have added a new {Code|import} statement for {Code|Element} module (similar to how we did it for {Code|Svg} and {Code|Svg.Attributes} before).
+
+ After imports our code looks the same at first: {Code|main} is an {Code|svg} element containing the {Code|circle}, but we have added two new attributes {Code|width} an {Code|height}, both set to {Code|"100%"}. We will talk more about attributes later today.
+
+ Then there are some new things on lines 19 - 23. First is the pipe operator ({Code|\|>}). We use it to pass the {Code|svg} element together with the {Code|circle} inside it into `Element.html` function (we need to convert it into an {Code|Element}) and then to pass this {Code|Element} into an {Code|Element.layout} which will fill the viewport with its contents. Again, more about the pipe and functions on day 3.
+
+Reload the browser. The SVG space now fills the entire viewport. Even the small white margin is gone!
=
=| Window
= | DotInElement
@@ -253,7 +277,22 @@ Reload the browser to confirm your hypothesis. Voila! The SVG space now fills th
= cx=0
= cy=0
=
-It should now be possible to place our dot in the center of the screen. But, how exactly will we achieve it?
+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.
+
+| Header
+ The Coordinates
+
+We will now explore how child elements (like our {Code|circle}) are positioned inside the parent {Code|svg} element.
+
+One method is to use the *cartesian coordinates system*. To get some intuition about it, imagine that you are a villain in an old James Bond movie. For some sinister purpose you need to tell somebody exactly where a flower pot is located on a large table. The problem is that you can only communicate through an old fashioned phone {Icon|name=phone}. To fool mr. Bond you need to be very precise, so saying something like "it's roughly in the middle" won't work.
+
+| Note
+ TODO: Image of the table, the flower pot and the villain
+
+However, if you had a ruler, you could say: "it's 63.3cm from the top edge and 27.1cm from the left edge". To do that you would have previously agree which edge is left and which is top - not so obvious in case of a table. Fortunately in SVG we have this sort of agreement made for us. We alsways measure from the top and left edge of the viewport.
+
+The distance from the left edge is called {Code|x} and the distance from the top is called {Code|y}. The bigger the {Code|x} the more to the right goes the element. The bigger the {Code|y} the more it goes down. Try it out:
+
=| Row
= | CartesianCoordinates
= minX = -10
@@ -261,16 +300,11 @@ It should now be possible to place our dot in the center of the screen. But, how
= maxX = 300
= maxY = 200
=
-Let's return to the idea we introduced earlier, that the center of the dot is at a point called the {Code|origin} or {Code|(0, 0)}. What exactly does this mean?
-
-* a picture of a vase on a table should be here *
-
-Adjust the sliders below to change the x and y coordinates of the dot. It's like moving an object on a table by describing how far left, right, up or down it should be.
-
+Adjust the sliders below to change the {Code|x} and {Code|y} coordinates of the dot. It's like moving the flower pot on the table by describing how far left, right, up or down it should be.
=
-Knowing that the center of the dot is at the {Code|origin} or point {Code|(0,0)} of our plain, we can see that by default the origin of an SVG space is at the upper left corner. If we want to help our dot get out of hiding, we'll have to place the center of the dot at a point with positive {Code|x} and {Code|y} values.
+Initially the center of the dot is at a point called /the origin/ or {Code|\{ x = 0, y = 0\}}. The origin is in the top left corner of the viewport. That's why only quarter of the dot is visible. If we want to help our dot get out of hiding, we'll have to place the center of the dot at a point with positive {Code|x} and {Code|y} values.
=
-To change the position of the dot, we can use the {Code|cx} and {Code|cy} attributes of the {Code|circle} element.
+To change the position of the dot, we can use the {Code|cx} and {Code|cy} attributes of the {Code|circle} element, like this:
=
=| Code
= module Main exposing (main)
@@ -281,25 +315,23 @@ To change the position of the dot, we can use the {Code|cx} and {Code|cy} attrib
=
=
= main =
- Element.layout
+ [ Svg.circle
+ [ Svg.Attributes.r "10"
+ , Svg.Attributes.cx "100"
+ , Svg.Attributes.cy "50"
+ ]
+ []
+ ]
+ |> Svg.svg
+ [ Svg.Attributes.height "100%"
+ , Svg.Attributes.width "100%"
+ , Svg.Attributes.style "background: pink"
+ ]
+ |> Element.html
+ |> Element.layout
= [ Element.width Element.fill
= , Element.height Element.fill
= ]
- (Element.html
- (Svg.svg
- [ Svg.Attributes.height "100%"
- , Svg.Attributes.width "100%"
- , Svg.Attributes.style "background: pink"
- ]
- [ Svg.circle
- [ Svg.Attributes.r "10"
- , Svg.Attributes.cx "100"
- , Svg.Attributes.cy "50"
- ]
- []
- ]
- )
- )
=
=| Window
= | DotInElement
@@ -308,30 +340,49 @@ To change the position of the dot, we can use the {Code|cx} and {Code|cy} attrib
= cx=100
= cy=50
=
-Now, if we want our dot to be exactly in the center of the screen, we'll have to set {Code|cx} and {Code|cy} to values equal to exactly half of the {Code|width} and {Code|height} of the SVG space.
+| 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.
+
=
-We can calculate the position of the circle as:
+Now the dot is not in the corner anymore, but it's not in the center yet. If we want our dot to be exactly in the center of the screen, we'll have to set {Code|cx} and {Code|cy} to values equal to exactly half of the {Code|width} and {Code|height} of the SVG viewport. We could calculate the position of the circle as:
=
=| Monospace
- cx = width/2
- cy = height/2
+ cx = width / 2
+ cy = height / 2
=
-There is a problem though! We don't actually know the width and height of the SVG space. All we know is that out {Code|layout's} {Code|width} and {Code|height} will {Code|fill} the viewport, and the {Code|svg} space's {Code|width} and {Code|height} are {Code|100%} of the {Code|layout}. We don't know the exact size of either (because the {Code|layout} will scale to the browser window).
-☹️
+There is a problem though! We don't actually know the width and height of the SVG viewport. All we know is that it will fill all the available space, but this can be different on different devices: {Icon|name=smartphone} {Icon|name=tablet} {Icon|name=monitor}
=
-Fortunately, we don't need to know the width and height of the SVG space!
+Fortunately, we don't need to know the width and height of the {Code|svg} element! That's why it's called *scalable*. We can work with it without knowing the actual scale on the screen.
=
-This is a bit tricky. Instead of moving the dot in the SVG space, we can set the boundaries of the {Code|scene} so that the dot is at the center using a property called {Code|Viewbox}.
+| Header
+ The ViewBox
+
+You can imagine an SVG element as an infinite surface on which elements are placed. Which elements you see, depends on where you look. And where you look is called the *viewbox*. It has four properties: {Code|top}, {Code|left}, {Code|width} and {Code|height}.
+
+It works a little bit like a photo camera pointing directly at the surface. You can move it up, down (along the {Code|y} axis) and left and right (along the {Code|x} axis). You can also change it's focal length to cover more or less area. Unlike a lens of the camera you set the width and height separately. If you set it so that the dot is in the middle of your frame, then no matter how big the print of the picture will be, the dot will always be in the center. Increasing the {Code|width} and {Code|height} of the viewbox will make more of the surrounding surface visible and the dot will become smaller, but it's position won't change. Let's see it in action:
+
+| Row
+ | ViewBox
=
-To use an anology, imagine you've decided to build a holiday home with your family or friends in the Alps. Sounds pretty nice, right? Now, your budget isn't unlimited, so you've had to make a few compromises. You're in a beautiful area with a lovely view of the mountains, but yours isn't the only house in the area, and some of the others aren't the nicest to look at. Of course, you don't want your view of the beautiful snow-capped mountains to be blocked by a big pile of concrete. So what are you going to do about this? You can move your neighbours' homes, and the mountains while you're at it, so they are directly in front of your windows. Or, you can move the windows as you design your house so that you get a full view of the beautiful surroundings. Imagine you could move the windows around, as you decide the best place for them. As the window moves, you it will look as though the surroundings as moving, to the right, the left, up and down, as you shift the window. But in reality it is only your perspective that is changing.
=
-A videwbox is like a window into an SVG space. It allows us to change the point of view of the scene.
+On the left and the right side we show the same set of elements: a dot at the origin and two axes ({Code|x} going from left to right and {Code|y} going top - down). The pink rectangle represents the viewbox. Normally it is not visible, but we display it here to help you understand how it works.
+
+On the right there is a window with a viewport inside. Everything covered by the viewbox will be displayed in the viewport. Their sizes are independent, so the shapes will be scaled to fit. You can also set the option to preserve the aspect ratio to prevent the shapes from being stretched.
+
+You cannot control the size of the window or the viewport. All you can do is play with the viewbox. Try calibrating the {Code|left}, {Code|top}, {Code|width} and {Code|height} properties of the viewbox so that the dot is in the center of the viewport.
+
+| Note
+ Here is how I would do it. First pick any number for the {Code|width}, say {Code|20}. Then you have to move the left edge half way ({Code|20 // 2 = 10}) to the left. Once you click on a slider you can use {Key|◀} and {Key|▶} keys to precisely set the value. Moving to the left means making the value lesser, and since we start from {Code|0} we will end up at {Code|-10}. Do the same for {Code|height} and {Code|top}. The dot should be exactly in the center of the viewbox and the viewport.
+
+The formula is simple:
=
-| ViewBox
+| Monospace
+ left = - (width / 2)
+ top = - (height / 2)
=
-In this example, we can set the width and height of the viewbox to an arbitrary value. As we see in the interactive example, the width and height of the viewbox will effect how big the dot appears, but that's not so important to us at the moment. Let's set them both to 600.
+So instead of moving the dot in the SVG plane, we change where we look by moving the viewbox. By setting it that way, we know that the distance from the left and right edge of the screen to the center of the dot will be the same. We don't know exactly what it will be (because we don't control the size of the viewport), but it will always be equal. That's the definition of being in the center!
=
-The top and left values of the viewbox are a bit trickier. We saw ealier that if we knew the width and height of our SVG scene, we could have set the cx and cy values of the dot to half the width and height of the scene to center the dot. We will have to use similar reasoning here. If we set the top and left values of the viewbox to 0, the dot will still still appear in the top left corner of the scene. If we want to see the dot moved to the right and downward within the scene, we will need the top and left values of the viewbox to be negative. If we want the origin of the SVG space to appear directly in the center of the scene, we will need to set the top and left values of the viewbox to {Code|- height//2} and {Code|- width//2}. In this case, they will both have the value 300.
+In the code we set the viewbox as an attribute of the {Code|svg} element, like this:
=
=| Code
= module Main exposing (main)
@@ -342,240 +393,84 @@ The top and left values of the viewbox are a bit trickier. We saw ealier that if
=
=
= main =
- Element.layout
+ [ Svg.circle
+ [ Svg.Attributes.r "10"
+ , Svg.Attributes.cx "0"
+ , Svg.Attributes.cy "0"
+ ]
+ []
+ ]
+ |> Svg.svg
+ [ Svg.Attributes.height "100%"
+ , Svg.Attributes.width "100%"
+ , Svg.Attributes.style "background: pink"
+ , Svg.Attributes.viewBox "-300 -300 600 600"
+ ]
+ |> Element.html
+ |> Element.layout
= [ Element.width Element.fill
= , Element.height Element.fill
= ]
- (Element.html
- (Svg.svg
- [ Svg.Attributes.viewBox "-300 -300 600 600" ]
- [ Svg.circle [ Svg.Attributes.r "10" ] [] ]
- )
- )
=
-Refresh the broswer. We should now see the dot in the center of the screen.
+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=none
+ background=pink
= fill=black
= viewBox=-300 -300 600 600
=
+You can also remove line 19 to remove the pink background, or set it to some other color.
=
-Now that we've centered our dot, we can customize it by giving it a color! Let's take a deeper look at how we give attributes to an SVG element.
-
-We've already seen a few example of SVG attributes. The radius of our dot ({Code|circle}) is an attribute.
-
-| Code
- Svg.circle [ Svg.Attributes.r "10" ] []
-
-Try giving it a new value to see your dot grow or shrink.
-
-Our viewBox is also an attribute. It's an attribute of the {Code|svg} element.
-
-| Code
- Svg.svg
- [ Svg.Attributes.viewBox "-300 -300 600 600" ]
- [ Svg.circle [ Svg.Attributes.r "10" ] [] ]
-
-Again, try giving it new values. As we saw earlier, the first two values (which represent the left and top of the viewBox) must be equal to the negative of the half of the last two values (which correspond to width and height). Try other combinations of values to adjust the placement of the dot. If the behavior of the viewBox is difficult to understand, refer again to the interactive tutorial above.
-
-To color our dot, we'll have to give it a second attribute. Many SVG elements take a {Code|List} of attributes. A {Code|List} is a very important concept in software development. {Code|Lists} allow us to group an arbitary number of similar values. In this case, we'll be using a {Code|List} to group {Code|Attribute a} values. Whenever you see {Code|[...]} in an Elm source code file, you are dealing with a list.
-
-Before we give a color to our dot, try to identify all the lists in the code we're writen already. There are 5. Hint: It is possible for a list to have only 1 or 0 items ({Code|[]}).
-
-| Code
- Svg.circle [ Svg.Attributes.r "10" ] []
-
-When we take a look at the code responsible for creating our dot, we see that it is followed by two lists. We'll only need to worry about the first one at the moment. As we've discussed, the first list already contains the {Code|r} attribute of the circle, representing the radius. We'll need to add a second attribute to the circle. We use a comma ({Code|,}) to seperate elements in a list.
-
-| Code
- Svg.circle [ Svg.Attributes.r "10", ... ] []
+| Header
+ Color
=
-To color our circle, we'll use the {Code|fill} attribute.
+Speaking of colors, now that we've centered our dot, it's time to give it a color! We do it by adding an {Code|fill} attribute to the {Code|circle} element, like that:
=
=| Code
- Svg.circle
- [ Svg.Attributes.r "10"
- , Svg.Attributes.fill "blue"
- ]
- []
-
-You should see this result:
-
-| Window
- | DotWithViewBox
- background=none
- fill=blue
- viewBox=-300 -300 600 600
-
+ module Main exposing (main)
=
-Let's use our new understanding of lists to do something a bit more interesting. Let's draw multiple dots!
+ import Element
+ import Svg
+ import Svg.Attributes
=
-We can start by taking a look at our {Code|main} value:
=
-| Code
= main =
- Element.layout
+ [ Svg.circle
+ [ Svg.Attributes.r "10"
+ , Svg.Attributes.cx "0"
+ , Svg.Attributes.cy "0"
+ , Svg.Attributes.fill "skyblue"
+ ]
+ []
+ ]
+ |> Svg.svg
+ [ Svg.Attributes.height "100%"
+ , Svg.Attributes.width "100%"
+ , Svg.Attributes.viewBox "-300 -300 600 600"
+ ]
+ |> Element.html
+ |> Element.layout
= [ Element.width Element.fill
= , Element.height Element.fill
= ]
- (Element.html
- (Svg.svg
- [ Svg.Attributes.viewBox "-500 -500 1000 1000"
- ]
- [ Svg.circle
- [ Svg.Attributes.r "10"
- , Svg.Attributes.fill "blue"
- ]
- []
- ]
- )
- )
-
-Where is our list of circles? To find it, first find the circle.
-
-| Code
- Svg.circle
- [ Svg.Attributes.r "10"
- , Svg.Attributes.fill "blue"
- ]
- []
=
-Next, identify the first open square bracket {Code|[} before the circle.
-
-| Code
- [ Svg.circle
- [ Svg.Attributes.r "10"
- , Svg.Attributes.fill "blue"
- ]
- []
- ...
-
-This is where the list begins.
-
-Finally, identify the matching closing brakcet {Code|]}. This is a bit tricky because there are two lists within this list. The first is the list of attributes to the circle we saw earlier. The second is an empty list. We'll see exactly what they do in a moment. Ignore both of these for now. The matching closing bracket should be on the same indentation level as the opening bracket. Your text editor should be able to help you here. When you move your cursor before the opening bracket, the closing bracket should be highlighted.
-
-| Code
- [ Svg.circle
- [ Svg.Attributes.r "10"
- , Svg.Attributes.fill "blue"
- ]
- []
- ]
-
-Right now, our list of circles has one value. We want to give it a second value. The second value will look identical to the first. What is the first value?
-
-| Code
- Svg.circle
- [ Svg.Attributes.r "10"
- , Svg.Attributes.fill "blue"
- ]
- []
-
-It might not seem obvious at first that this is all one value. But it is. {Code|Svg.circle} takes two lists as arguments. We'll see what an argument is later. For now, we will need to understand the difference between an attribute and a child.
-
-Imagine a box of colored easter eggs. One egg is painted yellow, another red, another blue. Moreover, they don't all come from the same bird. One is a small robin's egg. Another is a medium chicken egg. Another is a huge ostrich egg.
-
-Some of the elements in our image are 'things'. A box is a thing. Each egg is also a thing. Some of the elements in our image are not things. Blueness is not a thing. Medium-sizeness is not a thing. You can see blueness. You can see medium-sizeness. But they are not things. They are attributes of a thing. A thing can be blue. It can be medium-sized.
-
-It is a convention in many programming languages, including Elm and SVG, to distinguish between the attributes and children of an element. If we wanted to represent our box in SVG, we would give our box several attributes. It would have a color (maybe brown). It would have a width, length, depth. We would also give it several children. Each of the eggs within the box would be a child of the box. A box with 6 eggs would have 6 children. Each of these children (the eggs) would have different attributes: blue, red, medium-sized, etc. The children of an element are the 'things' it contains. The attributes of a thing are the elements that describe it.
-
-The first list following {Code|Svg.circle} is:
-
-| Code
- [ Svg.Attributes.r "10"
- , Svg.Attributes.fill "blue"
- ]
-
-This is a list of attributes. A radius is not a 'thing'. A fill color is not a 'thing'. They are elements that describe a thing. They describe our circle.
-
-The second list is:
-
-| Code
- []
-
-It is an empty list. Nevertheless, it is a list of children. Our circles don't have any children.
-
-Is the circle itself an attribute or a thing?
-
-From Elm's perspective, this entire code block is a circle:
-
-| Code
- Svg.circle
- [ Svg.Attributes.r "10"
- , Svg.Attributes.fill "blue"
- ]
- []
-
-This is not a circle:
-
-| Code
- Svg.circle
- [ Svg.Attributes.r "10"
- , Svg.Attributes.fill "blue"
- ]
-
-Nor is this:
-
-| Code
- Svg.circle
-
-To create a list with two circles, we'll want to duplicate the properly formed circle value. You can give the second dot a different color from the first.
-
-| Code
- [ Svg.circle
- [ Svg.Attributes.r "10"
- , Svg.Attributes.fill "blue"
- ]
- []
- , Svg.circle
- [ Svg.Attributes.r "10"
- , Svg.Attributes.fill "red"
- ]
- []
- ]
-
-Make add the second circle to the list of circles in your source code and refresh the page. What do you see?
+The only change is on line 13. Reload the browser and see this:
=
=| Window
- | TwoDots
+ | DotWithViewBox
= background=none
+ fill=skyblue
= viewBox=-300 -300 600 600
- firstFill=blue
- secondFill=red
- firstCX=0
- secondCX=0
-
-Only one dot! We see the second one, but what happened to the first dot? It's under the second... Both are at the origin (0, 0), so they are both in the same spot. If we want to see both dots, we will have to change their positions. To do this we'll have to use their {Code|cx} or {Code|cy} values.
=
-| Code
- [ Svg.circle
- [ Svg.Attributes.r "10"
- , Svg.Attributes.cx "-10"
- , Svg.Attributes.fill "blue"
- ]
- []
- , Svg.circle
- [ Svg.Attributes.r "10"
- , Svg.Attributes.cx "10"
- , Svg.Attributes.fill "red"
- ]
- []
- ]
+| Emphasize
+ Wow!
=
-| Window
- | TwoDots
- background=none
- viewBox=-300 -300 600 600
- firstFill=blue
- secondFill=red
- firstCX=-100
- secondCX=100
=
-As an exercise, try adding an {Code|Svg.Attribute.cy} attribute to the circles to adjust their vertical position.
+That's exactly what we were trying to achieve. Scroll up to the problem section to compare.
=
-For a more advanced exercise, try adding a new circle. Why not draw 5 circles on the screen? Give them each a unique color and position.
+| Emphasize
+ Congratulations!
=
=| Emphasize
= {Icon|name=award}index 1ab9957..5780836 100644
--- a/content/day-3.txt
+++ b/content/day-3.txt
@@ -4,9 +4,8 @@
=| Emphasize
= Connecting the Dots
=
-
=| Note
- Today we are going to lear about
+ So far we have been writing the code of our program without thinking too much about about what it means. Today we are going to learn about
=
= | List
= - Values and names
@@ -15,10 +14,400 @@
= - Types
= - SVG gradients
=
-| Header
- The Problem
+Before we begin, let's reflect on the following.
+
+| Emphasize
+ Every Elm program is composed of three basic building blocks: {Code|values}, {Code|names} and {Code|types}.
+
+For efficiency we are going to play with these concepts in REPL (the Read - Evaluate - Print Loop). Go to the terminal. If you have Elm reactor running, then stop it with {Key|ctrl} + {Key|c}. Then start the REPL by entering
+
+| Terminal
+ elm repl
+
+It should show something like this
+
+| Terminal
+ ---- Elm 0.19.0 ----------------------------------------------------------------
+ Read <https://elm-lang.org/0.19.0/repl> to learn more: exit, help, imports, etc.
+ --------------------------------------------------------------------------------
+ >
+
+Here you can enter snippets of Elm code and see it evaluated.
+
+| Note
+ To /evaluate/ means to compute the value of a given expression, e.g. expression {Code|2 + 2} has a value of {Code|4}. The process of figuring it out is called evaluation. That's what computer programs do!
+
+Let's start with values. The simplest way to create a value is to literally type it in our code. Try it (type things after {Code|>} and expect to see what follows):
+
+| Terminal
+ > 2
+ 2 : number
+ > "Hello"
+ "Hello!" : String
+ >
+
+These expressions ({Code|2} and {Code|"Hello!"}) are called /literals/, because their values are literally what they look like. The value of {Code|2} is simply {Code|2}! When you enter them, the REPL evaluates and prints their value (which in this case is very easy) and type (the text after {Code|:}). We will get to types later.
+
+Some value literals are simple, like numbers and strings. On day 1 we discussed lists. You can also type them literally, like this:
+
+| Terminal
+ > [ 1, 2, 5, 77, 2 ]
+ [1,2,5,77.6,2] : List number
+
+First notice that a list literal starts with {Code|[} and ends with {Code|]}. Inside there are values called /elements of the list/. Each element is separated from others with a {Code|,} (comma). Then notice that each element is a value on it's own, but the whole list is also a value.
+
+| Note
+ It can be called a /composite value/. It's like the box of eggs. Each egg is a thing, but the box of eggs is also a thing (composed of the box and the eggs).
+
+List can be empty:
+
+| Terminal
+ > []
+ [] : List a
+
+or contain only one element:
+
+| Terminal
+ > [ "Hello" ]
+ ["Hello"] : List String
+
+| Note
+ Notice that a list with one element is not the same as the element itself, just like the box with one egg is not the same as an egg alone.
+
+Now let's focus our attention to *names*. You can give any value a name.
+
+| Terminal
+ > kittens = 2
+ 2 : number
+ > greeting = "Hello!"
+ "Hello!" : String
+ >
+
+From now on you can refer to this value either literally or by it's name:
+
+| Terminal
+ > kittens
+ 2 : number
+ > 2
+ 2 : number
+
+The effect will be exactly the same - you will get a value back. So we see that there are at least two ways of getting values: by literally expressing them or refering to them by names given to them previously. In a composite value of a list, you can mix the two ways together:
+
+| Terminal
+ > [ 1, kittens, 3 ]
+ [1,2,3] : List number
+
+A function is a special kind of a value. Let's create a named function like this:
+
+| Terminal
+ > fun something = something ++ " is fun!"
+ <function> : String -> String
+
+Here we gave a name {Code|fun} to a function, that given {Code|something} will produce a string saying that this {Code|something} is fun! Let's see how you can use it:
+
+| Terminal
+ > fun "Learning Elm"
+ "Learning Elm is fun!" : String
+
+Above we applied the function (by calling it's name: {Code|fun}) to a literal value: {Code|"Learning Elm"}. In return the function produced another value: {Code|"Learning Elm is fun!"}. So a function takes a value and gives a value. But function itself is also a value.
+
+| Note
+ Imagine a machine that paints eggs. You put an egg into the machine and on the other side you get a painted egg. The egg is a thing and the painted egg is also a thing. But so is the machine!
+
+Now we see that we can get a value in three ways: (1) by literally expressing it, (2) by calling it's name and (3) by applying a function to another value.
+
+Not every function can be applied to any value. You can add two numbers (like {Code|2} and {Code|5} in {Code|2 + 5}), but you can't a number to a string ({Code|2 + "Hello!"} - try it and see an error!). The system that governs what function can be applied to what value is called a *type system* and it plays important role in Elm programming.
+
+| Note
+ Again, imagine a machine that paints eggs. If you try to put a carrot in that machine, the dumb machine would make a mess of your carrot and probably jam. But a smart machine would sound a low pitch buzz and simply refuse to take the carrot. That way a smart machine avoids making a mess and possibly damaging itself. The Elm's type system is what makes your functions smart. It stops you from putting a carrot into the egg painting machine!
+
+Every value in Elm has a type. You can see the type in the REPL - after evaluating the expression it shows it's value and the type, like here:
+
+| Terminal
+ > kittens
+ 2 : number
+
+Above {Code|2} is the value and `number` is the type. Similar here:
+
+| Terminal
+ > fun "Learning Elm"
+ "Learning Elm is fun!" : String
+
+The value is {Code|"Learning Elm is fun!"} and the type is {Code|String}.
+
+Composite values like lists have composite types. For example here the type is {Code|List String}.
+
+| Terminal
+ > [ "Hello", "Good bye" ]
+ ["Hello","Good bye"] : List String
+
+This means that it's a list of strings. That means that all the elements of this list are of type {Code|String}. When talking about lists you have to tell that it's a list and what is the type of it's element. Only this can be considered a fully specified type. Compare:
+
+| Terminal
+ > [ 1, 2, 3 ]
+ [1,2,3] : List number
+
+This is a list of numbers. It's easy to see - all the elements are numbers, just as previously they were strings. All the elements of a list must be of the same type. If you try mixing the types, Elm will refuse to build your program. Try:
+
+| Terminal
+ > [ 1, 2, 3, "Hello" ]
+ -- TYPE MISMATCH ----------------------------------------------------------- elm
+
+ The 4th element of this list does not match all the previous elements:
+
+ 7| [ 1, 2, 3, "Hello" ]
+ ^^^^^^^
+ The 4th element is a string of type:
+
+ String
+
+ But all the previous elements in the list are:
+
+ number
+
+ Hint: Everything in the list needs to be the same type of value. This way you
+ never run into unexpected values partway through. To mix different types in a
+ single list, create a "union type" as described in:
+ <http://guide.elm-lang.org/types/union_types.html>
+
+
+What's the type of an empty list? Let's see:
+
+| Terminal
+ > []
+ [] : List a
+
+A list of {Code|a}. Obviously {Code|a} is not a type. This means that the type of the elements cannot be determine yet. This is signaled by the lowercase term where the type should be: {Code|a} instead of the capitalized {Code|String}.
+
+| Note
+ Notice that the {Code|number} (type of value {Code|15} for example} is also lowercase. That's because it's also not a concrete type! Elm has two concrete types for numbers: {Code|Int} that can represent an integer number (1, 2, 3, 0, -122 etc.) and {Code|Float} that can represent a number with a fraction, (like 1.2, -3.5, 44.2). Because fraction part can be 0 (e.g. 1.0, 2.0, 0.0, -122.0 - so called whole numbers), it's not possible to tell if {Code|2} is an {Code|Int} or a {Code|Float}. We will see some of the implications of this later.
+
+Above I said that every value has a type and also that a function is a value on it's own. Let's try the following in REPL:
+
+| Terminal
+ > fun
+ <function> : String -> String
+
+We have asked the REPL to evaluate the value referenced by name {Code|fun}. In response the value is display as {Code|<function>}. That's because there is no simple way to represent the value of a function as text. Let's focus our attention on the type (part after the colon {Code|:}). It is {Code|String -> String}. This means that the type is a function (we recognize it by the arrow symbol). What's before the arrow is a type of the value that goes into the function (called the /argument of the function/). The type of the argument is {Code|String}. That shouldn't be a surprise. Remember how we have used the function before to produce the value:
+
+| Terminal
+ > fun "Learning Elm"
+ "Learning Elm is fun!" : String
+
+The part expressed as {Code|"Learning Elm"} is a {Code|String} (we recognize it by the quotation around it) and that's exactly what the function needs.
+
+The part after the arrow symbol is the type that the function returns. It is also {Code|String}. That also should not surprise us, since {Code|"Learning Elm is fun!"} is also a {Code|String}.
+
+Not every function returns the same type as it takes. For example function {Code|String.fromInt} takes a value of type {Code|Int} and returns a value of type {Code|String}. It's type is therefor {Code|Int -> String}. We read it as: /type of String.fromInt is a function from Int to String/. Let's see it in action:
+
+| Terminal
+ > String.fromInt 10
+ "10" : String
+
+That's right! The value {Code|10} is an {Code|Int}, the value {Code|"10"} is a {Code|String}.
+
+| Terminal
+ > String.fromInt
+ <function> : Int -> String
+
+But what if we want to say that a certain number (say, {Code|232}) is fun? We have our {Code|fun} function, but it takes a {Code|String} and what we have is an {Code|Int}. We can use the very same {Code|String.fromInt} to first convert it into the {Code|String} and then pass the value to the {Code|fun} function, like this:
+
+| Terminal
+ > fun (String.fromInt 232)
+ "232 is fun!" : String
+
+We had to put the {Code|String.fromInt 232} expression in parentheses, so that Elm understands that value {Code|232} is to be passed to {Code|String.fromInt} function, instead of the {Code|String.fromInt} being passed to {Code|fun} (which would not work anyway, since {Code|fun} takes values of type {Code|String}, not {Code|Int -> String}).
+
+Alternatively we can use the pipe operator which looks like this: {Code|\|>}. It has the advantage that the code that the transformations that happen first are on the left side and ones that happen later on the right, so we can read our code left to right instead of inside out. Here is how it would look like:
+
+| Terminal
+ > 232 |> String.fromInt |> fun
+ "232 is fun!" : String
+
+Now it's more apparent that the value {Code|232} is passed to the function {Code|String.fromInt}, and whatever comes on the other end is passed to the {Code|fun} function. We don't need parentheses in this case.
+
+| Note
+ TODO: But where does the functions come from? Modules.
+
+
+
+
+
=
=| Note
= *We are still working on this content.*
=
= Stay tuned {Icon|name=radio}
+
+Let's take a deeper look at how we give attributes to an SVG element.
+
+We've already seen a few example of SVG attributes. The radius of our dot ({Code|circle}) is an attribute.
+
+| Code
+ Svg.circle [ Svg.Attributes.r "10" ] []
+
+Try giving it a new value to see your dot grow or shrink.
+
+Our viewBox is also an attribute. It's an attribute of the {Code|svg} element.
+
+| Code
+ Svg.svg
+ [ Svg.Attributes.viewBox "-300 -300 600 600" ]
+ [ Svg.circle [ Svg.Attributes.r "10" ] [] ]
+
+Again, try giving it new values. As we saw earlier, the first two values (which represent the left and top of the viewBox) must be equal to the negative of the half of the last two values (which correspond to width and height). Try other combinations of values to adjust the placement of the dot. If the behavior of the viewBox is difficult to understand, refer again to the interactive tutorial above.
+
+To color our dot, we'll have to give it a second attribute. Many SVG elements take a {Code|List} of attributes. A {Code|List} is a very important concept in software development. {Code|Lists} allow us to group an arbitary number of similar values. In this case, we'll be using a {Code|List} to group {Code|Attribute a} values. Whenever you see {Code|[...]} in an Elm source code file, you are dealing with a list.
+
+Before we give a color to our dot, try to identify all the lists in the code we're writen already. There are 5. Hint: It is possible for a list to have only 1 or 0 items ({Code|[]}).
+
+| Code
+ Svg.circle [ Svg.Attributes.r "10" ] []
+
+When we take a look at the code responsible for creating our dot, we see that it is followed by two lists. We'll only need to worry about the first one at the moment. As we've discussed, the first list already contains the {Code|r} attribute of the circle, representing the radius. We'll need to add a second attribute to the circle. We use a comma ({Code|,}) to separate elements in a list.
+
+| Code
+ Svg.circle [ Svg.Attributes.r "10", ... ] []
+
+To color our circle, we'll use the {Code|fill} attribute.
+
+| Code
+ Svg.circle
+ [ Svg.Attributes.r "10"
+ , Svg.Attributes.fill "blue"
+ ]
+ []
+
+You should see this result:
+
+| Window
+ | DotWithViewBox
+ background=none
+ fill=blue
+ viewBox=-300 -300 600 600
+
+Let's use our new understanding of lists to do something a bit more interesting. Let's draw multiple dots!
+
+We can start by taking a look at our {Code|main} value:
+
+| Code
+ main =
+ Element.layout
+ [ Element.width Element.fill
+ , Element.height Element.fill
+ ]
+ (Element.html
+ (Svg.svg
+ [ Svg.Attributes.viewBox "-500 -500 1000 1000"
+ ]
+ [ Svg.circle
+ [ Svg.Attributes.r "10"
+ , Svg.Attributes.fill "blue"
+ ]
+ []
+ ]
+ )
+ )
+
+Where is our list of circles? To find it, first find the circle.
+
+| Code
+ Svg.circle
+ [ Svg.Attributes.r "10"
+ , Svg.Attributes.fill "blue"
+ ]
+ []
+
+Next, identify the first open square bracket {Code|[} before the circle.
+
+| Code
+ [ Svg.circle
+ [ Svg.Attributes.r "10"
+ , Svg.Attributes.fill "blue"
+ ]
+ []
+ ...
+
+This is where the list begins.
+
+Finally, identify the matching closing brakcet {Code|]}. This is a bit tricky because there are two lists within this list. The first is the list of attributes to the circle we saw earlier. The second is an empty list. We'll see exactly what they do in a moment. Ignore both of these for now. The matching closing bracket should be on the same indentation level as the opening bracket. Your text editor should be able to help you here. When you move your cursor before the opening bracket, the closing bracket should be highlighted.
+
+| Code
+ [ Svg.circle
+ [ Svg.Attributes.r "10"
+ , Svg.Attributes.fill "blue"
+ ]
+ []
+ ]
+
+Right now, our list of circles has one value. We want to give it a second value. The second value will look identical to the first. What is the first value?
+
+| Code
+ Svg.circle
+ [ Svg.Attributes.r "10"
+ , Svg.Attributes.fill "blue"
+ ]
+ []
+
+It might not seem obvious at first that this is all one value. But it is. {Code|Svg.circle} takes two lists as arguments. We'll see what an argument is later. For now, we will need to understand the difference between an attribute and a child.
+
+Imagine a box of colored easter eggs. One egg is painted yellow, another red, another blue. Moreover, they don't all come from the same bird. One is a small robin's egg. Another is a medium chicken egg. Another is a huge ostrich egg.
+
+Some of the elements in our image are 'things'. A box is a thing. Each egg is also a thing. Some of the elements in our image are not things. Blueness is not a thing. Medium-sizeness is not a thing. You can see blueness. You can see medium-sizeness. But they are not things. They are attributes of a thing. A thing can be blue. It can be medium-sized.
+
+It is a convention in many programming languages, including Elm and SVG, to distinguish between the attributes and children of an element. If we wanted to represent our box in SVG, we would give our box several attributes. It would have a color (maybe brown). It would have a width, length, depth. We would also give it several children. Each of the eggs within the box would be a child of the box. A box with 6 eggs would have 6 children. Each of these children (the eggs) would have different attributes: blue, red, medium-sized, etc. The children of an element are the 'things' it contains. The attributes of a thing are the elements that describe it.
+
+The first list following {Code|Svg.circle} is:
+
+| Code
+ [ Svg.Attributes.r "10"
+ , Svg.Attributes.fill "blue"
+ ]
+
+This is a list of attributes. A radius is not a 'thing'. A fill color is not a 'thing'. They are elements that describe a thing. They describe our circle.
+
+The second list is:
+
+| Code
+ []
+
+It is an empty list. Nevertheless, it is a list of children. Our circles don't have any children.
+
+Is the circle itself an attribute or a thing?
+
+From Elm's perspective, this entire code block is a circle:
+
+| Code
+ Svg.circle
+ [ Svg.Attributes.r "10"
+ , Svg.Attributes.fill "blue"
+ ]
+ []
+
+This is not a circle:
+
+| Code
+ Svg.circle
+ [ Svg.Attributes.r "10"
+ , Svg.Attributes.fill "blue"
+ ]
+
+Nor is this:
+
+| Code
+ Svg.circle
+
+To create a list with two circles, we'll want to duplicate the properly formed circle value. You can give the second dot a different color from the first.
+
+| Code
+ [ Svg.circle
+ [ Svg.Attributes.r "10"
+ , Svg.Attributes.fill "blue"
+ ]
+ []
+ , Svg.circle
+ [ Svg.Attributes.r "10"
+ , Svg.Attributes.fill "red"
+ ]
+ []
+ ]index c2832b6..f181d18 100644
--- a/content/index.txt
+++ b/content/index.txt
@@ -13,7 +13,7 @@ Below is material through which we will be going during the workshop. Only the f
=
=| List
= # {Link|Before the course begins|url=/preparation.html}
- # {Link|Day 1 - Let's Make some Dots|url=/day-1.html}
+ # {Link|Day 1 - Let's Make a Dot|url=/day-1.html}
= # {Link|Day 2 - Let's Place the Dots in a Circle|url=/day-2.html}
= # {Link|Day 3 - Connecting the Dots|url=/day-3.html}
= # {Link|Day 4 - Let's Make a Tree|url=/day-4.html}Temporary fix for #9
On by
In capture.coffee preplace all p tags with div.
Upon closer inspection it seems that p tag is never targeted in css sheet. Only classes are targeted. so replacing it with div doesn't affect any styling.
I consider this a temporary solution. Permanent one probably requires changes to github.com/mdgriffith/elm-ui
index bdbd6c6..d630128 100644
--- a/src/capture.coffee
+++ b/src/capture.coffee
@@ -38,13 +38,16 @@ do () =>
= await page.goto url, waitUntil: "networkidle2"
=
= html =
- (await page.content ``).replace "</body>", """
- <script src="/assets/index.js"></script>
- <script>
- Elm.Main.init()
- </script>
- </body>
- """
+ (await page.content ``)
+ .replace /<p\b/gi, "<div"
+ .replace /<\/p\b/gi, "</div"
+ .replace "</body>", """
+ <script src="/assets/index.js"></script>
+ <script>
+ Elm.Main.init()
+ </script>
+ </body>
+ """
=
= await new Promise (resolve, reject) ->
= FS.writeFile path, html, (error) ->Use external stylesheet to load Google fonts to avoid FOAC
On by
Use Montserrat font for all texts except Monospace, Code and Terminal.
Justify text in paragraphs.
index fb384fe..1f43711 100644
--- a/container.html
+++ b/container.html
@@ -3,10 +3,13 @@
= <head>
= <meta charset="utf-8">
= <title>Software Garden</title>
+ <style>
+ @import url('https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Source+Code+Pro:300,400,700');
+ </style>
= </head>
= <body>
= <!-- <div id="app-container"></div> -->
- <script src="/assets/index.js"></script>
+ <script src="/built/index.js"></script>
= <script>
= Elm.Main.init()
= </script>index 1330ef5..b3c69f2 100755
--- a/scripts/build
+++ b/scripts/build
@@ -14,7 +14,7 @@ npx elm make src/Main.elm --output built/index.js
=
=cp -r content/ public/content/
=cp -r assets/ public/assets/
-cp -r built/* public/assets/
+cp -r built/ public/built/
=cp -r .well-known/ public/.well-known/
=
=index 987f698..d3e4036 100755
--- a/scripts/develop
+++ b/scripts/develop
@@ -9,4 +9,10 @@ do
= npx elm make --output "public/${example}.html" "${example}"
=done
=
-npx elm-live src/Main.elm --pushstate --port 8001 -- --debug
+npx elm-live src/Main.elm \
+ --pushstate \
+ --start-page container.html \
+ --port 8001 \
+ -- \
+ --output built/index.js \
+ --debugindex 782b5a7..64059a0 100644
--- a/src/Main.elm
+++ b/src/Main.elm
@@ -302,6 +302,7 @@ view model =
= [ -- Below is a hack that enables static site generation
= Element.htmlAttribute (Html.Attributes.id "app-container")
= , Element.htmlAttribute (Html.Attributes.lang "en")
+ , Font.family [ Font.typeface "Montserrat", Font.sansSerif ]
= ]
= content.body
= ]index 0a42947..18fef27 100644
--- a/src/Mark/Custom.elm
+++ b/src/Mark/Custom.elm
@@ -55,6 +55,7 @@ paragraph =
= [ Element.paddingXY 0 10
= , Element.spacing 12
= , css "hyphens" "auto"
+ , Font.justify
= ]
= (content model)
= in
@@ -71,7 +72,7 @@ monospace =
= , Font.size 16
= , Font.color colors.maroon
= , Font.family
- [ sourceCodePro
+ [ Font.typeface "Source Code Pro"
= , Font.monospace
= ]
= , Element.scrollbarY
@@ -95,7 +96,7 @@ code =
= , Border.color colors.charcoal
= , css "page-break-inside" "avoid"
= , Font.family
- [ sourceCodePro
+ [ Font.typeface "Source Code Pro"
= , Font.monospace
= ]
= , css "page-break-inside" "avoid"
@@ -116,7 +117,7 @@ code =
= [ Element.width Element.fill
= , Font.size 16
= , Font.family
- [ sourceCodePro
+ [ Font.typeface "Source Code Pro"
= , Font.monospace
= ]
= ]
@@ -187,7 +188,7 @@ terminal =
= , Background.color colors.charcoal
= , css "page-break-inside" "avoid"
= , Font.family
- [ sourceCodePro
+ [ Font.typeface "Source Code Pro"
= , Font.monospace
= ]
= , css "page-break-inside" "avoid"
@@ -207,7 +208,7 @@ terminal =
= [ Element.width Element.fill
= , Font.size 16
= , Font.family
- [ sourceCodePro
+ [ Font.typeface "Source Code Pro"
= , Font.monospace
= ]
= ]
@@ -228,7 +229,7 @@ terminal =
= }
= , Font.size 14
= , Font.family
- [ sourceCodePro
+ [ Font.typeface "Source Code Pro"
= , Font.monospace
= ]
= , Font.color colors.gray
@@ -312,9 +313,10 @@ note =
=emphasize : Mark.Block (model -> Element msg)
=emphasize =
= let
- render element model =
+ render elements model =
= model
- |> element
+ |> elements
+ |> Element.paragraph []
= |> Element.el
= [ Element.spacing 30
= , Font.bold
@@ -326,7 +328,7 @@ emphasize =
= in
= Mark.block "Emphasize"
= render
- paragraph
+ text
=
=
=image : Mark.Block (model -> Element msg)
@@ -375,7 +377,7 @@ text =
= , drop
= ]
= , code =
- [ Font.family [ sourceCodePro, Font.monospace ]
+ [ Font.family [ Font.typeface "Source Code Pro", Font.monospace ]
= , css "font-size" "0.8em"
= , Font.color colors.maroon
= , Element.paddingEach
@@ -500,14 +502,6 @@ css property value =
= )
=
=
-sourceCodePro : Font.Font
-sourceCodePro =
- Font.external
- { url = "https://fonts.googleapis.com/css?family=Source+Code+Pro:200,400,700&subset=latin-ext"
- , name = "Source Code Pro"
- }
-
-
=colors =
= { maroon = Element.rgb 0.7 0 0
= , gray = Element.rgb 0.8 0.8 0.8index bdbd6c6..ae3b622 100644
--- a/src/capture.coffee
+++ b/src/capture.coffee
@@ -39,7 +39,7 @@ do () =>
=
= html =
= (await page.content ``).replace "</body>", """
- <script src="/assets/index.js"></script>
+ <script src="/built/index.js"></script>
= <script>
= Elm.Main.init()
= </script>Upgrade feathericons/elm-feather to 1.3.0
On by
index b321d9d..439720e 100644
--- a/elm.json
+++ b/elm.json
@@ -19,7 +19,7 @@
= "elm-community/maybe-extra": "5.0.0",
= "elm-community/result-extra": "2.2.1",
= "elm-explorations/markdown": "1.0.0",
- "feathericons/elm-feather": "1.2.0",
+ "feathericons/elm-feather": "1.3.0",
= "ianmackenzie/elm-geometry": "1.2.1",
= "ianmackenzie/elm-geometry-svg": "1.0.2",
= "mdgriffith/elm-markup": "2.0.5",Correct the vertical alignment of Key, Icon and inline Code
On by
After changing the font they were a little bit off.
index 18fef27..b49f7d3 100644
--- a/src/Mark/Custom.elm
+++ b/src/Mark/Custom.elm
@@ -377,8 +377,10 @@ text =
= , drop
= ]
= , code =
- [ Font.family [ Font.typeface "Source Code Pro", Font.monospace ]
- , css "font-size" "0.8em"
+ [ Font.family
+ [ Font.typeface "Source Code Pro"
+ , Font.monospace
+ ]
= , Font.color colors.maroon
= , Element.paddingEach
= { top = 3
@@ -409,8 +411,7 @@ icon =
= )
= |> Element.el
= [ Element.padding 4
-
- -- , Element.width (Element.px 12)
+ , css "vertical-align" "middle"
= ]
= )
= |> Mark.inlineString "name"
@@ -427,8 +428,8 @@ key =
= )
= |> Element.row
= [ Element.paddingEach
- { top = 6
- , bottom = 4
+ { top = 7
+ , bottom = 7
= , right = 8
= , left = 8
= }
@@ -436,7 +437,7 @@ key =
= , Border.rounded 3
= , Font.variant Font.smallCaps
= , Font.size 12
- , Element.moveUp 2
+ , Element.moveUp 4
= , Font.bold
= , Background.color colors.charcoal
= , Font.color colors.whiteMake sure the title always displays Software Garden
On by
One problem is that it's repeated twice on index document.
index 64059a0..2e6df38 100644
--- a/src/Main.elm
+++ b/src/Main.elm
@@ -151,7 +151,7 @@ view model =
= "Malformed response body: " ++ message
= )
= |> Element.text
- |> View "Software Garden : Error fetching content"
+ |> View "Error fetching content"
=
= ParseError deadEnds ->
= deadEndsView deadEnds
@@ -161,7 +161,7 @@ view model =
=
= loadingView : View
= loadingView =
- { title = "Software Garden"
+ { title = "Loading Content"
= , body =
= "Loading content..."
= |> Element.text
@@ -182,7 +182,7 @@ view model =
= , Element.padding 40
= , Element.spacing 20
= ]
- |> View "Software Garden : Parsing Errors"
+ |> View "Parsing Errors"
=
= deadEndElement : DeadEnd -> Element Msg
= deadEndElement { row, col, problem, contextStack } =
@@ -296,7 +296,7 @@ view model =
= ++ String.fromInt col
= )
= in
- { title = content.title
+ { title = "Software Garden : " ++ content.title
= , body =
= [ Element.layout
= [ -- Below is a hack that enables static site generationWIP: Create a general purpose Dots example
On by
Requirements:
- Specify any viewbox
- Specify any background
- Specify any number of dots, each having it's own
- cx
- cy
- radius
- color
See FIXME comment in Main.elm.
index 5cd1456..2068cdf 100644
--- a/content/day-2.txt
+++ b/content/day-2.txt
@@ -26,6 +26,17 @@ Previously we have created a program that displays five colorful dots. Today we
= radius = 0 1
= scatter = False
=
+| Dots
+ | Container
+ background = brown
+ viewBox = -200 -200 400 400
+
+ | Dot
+ cx = 10
+ cy = 20
+ color = blue
+ radius = 33
+
=| Header
= What does it mean to be placed on a circle?
=new file mode 100644
index 0000000..03cc7bd
--- /dev/null
+++ b/src/Examples/Dots.elm
@@ -0,0 +1,79 @@
+module Examples.Dots exposing
+ ( Config
+ , Container
+ , Dot
+ , main
+ , ui
+ )
+
+import Circle2d
+import Element exposing (Element)
+import Geometry.Svg
+import Html exposing (Html)
+import Point2d exposing (Point2d)
+import Svg exposing (Attribute, Svg)
+import Svg.Attributes
+
+
+type alias Config =
+ { container : Container
+ , dots : List Dot
+ }
+
+
+type alias Container =
+ { background : String
+ , viewBox : String
+ }
+
+
+type alias Dot =
+ { cx : Float
+ , cy : Float
+ , radius : Float
+ , color : String
+ }
+
+
+defaults : Config
+defaults =
+ { container =
+ { background = "none"
+ , viewBox = "-300 -300 600 600"
+ }
+ , dots =
+ [ { cx = 0
+ , cy = 0
+ , radius = 10
+ , color = "skyblue"
+ }
+ ]
+ }
+
+
+main : Html msg
+main =
+ ui defaults
+ |> Element.layout
+ [ Element.width Element.fill
+ , Element.height Element.fill
+ ]
+
+
+ui : Config -> Element msg
+ui { container, dots } =
+ let
+ dot : Dot -> Svg msg
+ dot { cx, cy, color, radius } =
+ ( cx, cy )
+ |> Point2d.fromCoordinates
+ |> Circle2d.withRadius radius
+ |> Geometry.Svg.circle2d [ Svg.Attributes.fill color ]
+ in
+ dots
+ |> List.map dot
+ |> Svg.svg
+ [ Svg.Attributes.viewBox container.viewBox
+ , Svg.Attributes.style <| "background: " ++ container.background
+ ]
+ |> Element.htmlindex 2e6df38..006af8a 100644
--- a/src/Main.elm
+++ b/src/Main.elm
@@ -22,12 +22,10 @@ import Examples
=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.DotInElement
+import Examples.Dots
=import Examples.Gradient
=import Examples.Line
-import Examples.MultipleDots as MultipleDots
=import Examples.NestedTransformations
=import Examples.PolarCoordinates
=import Examples.RosetteTypedTransformations
@@ -194,13 +192,13 @@ view model =
= ++ String.fromInt level
=
= Mark.InlineStart ->
- "Inline start"
+ "Expecting inline start"
=
= Mark.InlineEnd ->
- "Inline end"
+ "Expecting inline end"
=
= Mark.BlockStart ->
- "Block start"
+ "Expecting block start"
=
= Mark.Expecting what ->
= "Expecting " ++ what
@@ -232,22 +230,22 @@ view model =
= "Escape"
=
= Mark.EscapedChar ->
- "Escaped character"
+ "Escaped a character"
=
= Mark.Newline ->
- "Expecting newline"
+ "Expecting a newline"
=
= Mark.Space ->
- "Space"
+ "Expecting a space"
=
= Mark.End ->
- "End"
+ "Expecting an end"
=
= Mark.Integer ->
- "Integer"
+ "Expecting an integer"
=
= Mark.FloatingPoint ->
- "Floating point"
+ "ExpectingIndent a floating point number"
=
= Mark.InvalidNumber ->
= "Invalid number"
@@ -461,10 +459,8 @@ document =
=
= examples =
= [ counter
- , dot
= , dotInElement
- , dotWithViewBox
- , twoDots
+ , dots
= , circle
= , line
= , gradient
@@ -494,80 +490,75 @@ document =
= in
= Mark.stub "Counter" render
=
- dot : Mark.Block (Examples.Model -> Element Msg)
- dot =
- let
- render : Dot.Config -> Examples.Model -> Element Msg
- render config model =
- Dot.ui config
- |> Element.html
- |> Element.el
- [ Element.width (Element.px 300)
- , Element.height (Element.px 150)
- ]
- |> Element.el
- [ Element.padding 5 ]
- in
- Mark.record2 "Dot"
- Dot.Config
- (Mark.field "background" Mark.string)
- (Mark.field "fill" Mark.string)
- |> Mark.map render
-
= dotInElement : Mark.Block (Examples.Model -> Element Msg)
= dotInElement =
= let
= render config model =
- DotInElement.ui config
+ Examples.DotInElement.ui config
= in
= Mark.record4 "DotInElement"
- DotInElement.Config
+ 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
=
- dotWithViewBox : Mark.Block (Examples.Model -> Element Msg)
- dotWithViewBox =
- let
- render config model =
- DotWithViewBox.ui config
- in
- Mark.record3 "DotWithViewBox"
- DotWithViewBox.Config
- (Mark.field "background" Mark.string)
- (Mark.field "fill" Mark.string)
- (Mark.field "viewBox" Mark.string)
- |> Mark.map render
+ dots : Mark.Block (Examples.Model -> Element Msg)
+ dots =
+ {- FIXME: This produces parsing errors
+
+ Markup:
+
+ | Dots
+ | Container
+ background = brown
+ viewBox = -200 -200 400 400
+
+ | Dot
+ cx = 10
+ cy = 20
+ color = blue
+ radius = 33
+
+ Errors:
+ End at 36:1
+ Expecting newline at 36:1
+ Block start at 36:1
=
- twoDots : Mark.Block (Examples.Model -> Element Msg)
- twoDots =
+ TODO: Create a minimal reproducible example on Ellie and report issue at github.com/mdgriffith/elm-markup
+ -}
= let
+ render :
+ Examples.Dots.Config
+ -> Examples.Model
+ -> Element Msg
= render config model =
- MultipleDots.ui config
+ Examples.Dots.ui config
+
+ container : Mark.Block Examples.Dots.Container
+ container =
+ Mark.record2 "Container"
+ Examples.Dots.Container
+ (Mark.field "background" Mark.string)
+ (Mark.field "viewBox" Mark.string)
+
+ dot : Mark.Block Examples.Dots.Dot
+ dot =
+ Mark.record4 "Dot"
+ Examples.Dots.Dot
+ (Mark.field "cx" Mark.float)
+ (Mark.field "cy" Mark.float)
+ (Mark.field "radius" Mark.float)
+ (Mark.field "color" Mark.string)
= in
- Mark.record6 "TwoDots"
- (\background viewBoxConf firstFill secondFill firstCX secondCX ->
- MultipleDots.Config background
- viewBoxConf
- [ [ Svg.Attributes.r "10"
- , Svg.Attributes.fill firstFill
- , Svg.Attributes.cx firstCX
- ]
- , [ Svg.Attributes.r "10"
- , Svg.Attributes.fill secondFill
- , Svg.Attributes.cx secondCX
- ]
- ]
+ Mark.block "Dots"
+ render
+ (Mark.startWith
+ Examples.Dots.Config
+ container
+ (Mark.manyOf [ dot ])
= )
- (Mark.field "background" Mark.string)
- (Mark.field "viewBox" Mark.string)
- (Mark.field "firstFill" Mark.string)
- (Mark.field "secondFill" Mark.string)
- (Mark.field "firstCX" Mark.string)
- (Mark.field "secondCX" Mark.string)
- |> Mark.map render
=
= circle : Mark.Block (Examples.Model -> Element Msg)
= circle =Create Protractor example and display protractor in Circle example
On by
My first real useful code 🥰
Co-Authored-By: Sam Phillips samuel.rodney.phillips@gmail.com
index 5cd1456..6ffd9ca 100644
--- a/content/day-2.txt
+++ b/content/day-2.txt
@@ -21,7 +21,7 @@ Previously we have created a program that displays five colorful dots. Today we
= | Circle
= dots = 5
= circle = 0 1
- angles = 0 1
+ protractor = False
= center = none
= radius = 0 1
= scatter = False
@@ -34,7 +34,7 @@ We all have some intuition about a circle. You can tell if you see one and if yo
=| Circle
= dots = 0
= circle = 1 0
- angles = 0 1
+ protractor = False
= center = none
= radius = 0 1
= scatter = False
@@ -45,7 +45,7 @@ But what is it that makes a circle what it is? First of all we have to realize t
=| Circle
= dots = 0
= circle = 1 0
- angles = 0 1
+ protractor = False
= center = red
= radius = 0 1
= scatter = False
@@ -55,7 +55,7 @@ We can say that four or more dots lay on a circle, if the distance to the center
=| Circle
= dots = 5
= circle = 3 3
- angles = 0 1
+ protractor = False
= center = red
= radius = 3 3
= scatter = False
@@ -119,7 +119,7 @@ Now, notice that the dots displayed by the program we are creating are not layin
= | Circle
= dots = 5
= circle = 0 1
- angles = 0 1
+ protractor = False
= center = none
= radius = 0 1
= scatter = True
@@ -127,7 +127,7 @@ Now, notice that the dots displayed by the program we are creating are not layin
= | Circle
= dots = 5
= circle = 0 1
- angles = 0 1
+ protractor = False
= center = none
= radius = 0 1
= scatter = False
@@ -138,7 +138,7 @@ On the right, you immediately see a patern. They form a circle. But in fact both
= | Circle
= dots = 5
= circle = 3
- angles = 0 1
+ protractor = False
= center = none
= radius = 0 1
= scatter = True
@@ -146,7 +146,7 @@ On the right, you immediately see a patern. They form a circle. But in fact both
= | Circle
= dots = 5
= circle = 3
- angles = 0 1
+ protractor = False
= center = none
= radius = 0 1
= scatter = False
@@ -168,9 +168,9 @@ A common way of representing rotation is in degrees. It works like this: imagine
=
=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:
=
-| Image
- src = /assets/protractor.svg
- description = A protractor by Georges Khaznadar <georgesk@ofset.org>
+| Protractor
+ 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.
@@ -206,6 +206,14 @@ Type {Code|:exit} to close the REPL.
=
=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.
=
+| Circle
+ dots = 5
+ circle = 3 3
+ protractor = True
+ center = red
+ radius = 3 3
+ scatter = False
+
=| Header
= The Code
=
@@ -215,7 +223,7 @@ Now that we understand what it means to be placed on a circle and evenly distrib
= | Circle
= dots = 5
= circle = 0 1
- angles = 0 1
+ protractor = False
= center = none
= radius = 0 1
= scatter = False
@@ -281,7 +289,7 @@ and {Link|open the program in your browser|url=http://localhost/src/Main.elm}. Y
= | Circle
= dots = 1
= circle = 0 1
- angles = 0 1
+ protractor = False
= center = none
= radius = 0 1
= scatter = False
@@ -435,7 +443,7 @@ In the browser it should look exactly as we planned:
= | Circle
= dots = 5
= circle = 0 1
- angles = 0 1
+ protractor = False
= center = none
= radius = 0 1
= scatter = Falseindex 35baf6c..eccb7d7 100644
--- a/src/Examples/Circle.elm
+++ b/src/Examples/Circle.elm
@@ -8,6 +8,7 @@ import Html exposing (Html)
=import List.Extra as List
=import Maybe.Extra as Maybe
=import Point2d
+import Protractor
=import Svg exposing (Svg)
=import Svg.Attributes
=import Transformations exposing (Transformation(..))
@@ -18,7 +19,7 @@ type alias Config =
= , circle : String -- Dash array
= , center : String -- Color
= , radius : String -- Dash array
- , angles : String -- Dash array
+ , protractor : Bool -- Display protractor?
= , scatter : Bool -- Are doot evenly distributed or scattered
= }
=
@@ -57,17 +58,19 @@ ui config =
= [ dots
= , center
= , circle
- , compass
= , radi
+ , protractor
= ]
=
- compass : Svg msg
- compass =
- 0
- :: angles
- |> pairs
- |> List.indexedMap arc
- |> Svg.g []
+ protractor =
+ case config.protractor of
+ True ->
+ Protractor.protractor { radius = 60, strokeWidth = 0.5 }
+
+ False ->
+ Svg.g
+ []
+ []
=
= pairs : List a -> List ( a, a )
= pairs list =
@@ -81,20 +84,6 @@ ui config =
= one :: two :: rest ->
= ( one, two ) :: pairs (two :: rest)
=
- arc : Int -> ( Float, Float ) -> Svg msg
- arc index ( start, end ) =
- Arc2d.with
- { centerPoint = Point2d.origin
- , radius = 30
- , startAngle = degrees start
- , sweptAngle = degrees (end - start)
- }
- |> Geometry.Svg.arc2d
- [ Svg.Attributes.stroke (color index)
- , Svg.Attributes.strokeDasharray config.angles
- , Svg.Attributes.fill "none"
- ]
-
= dots =
= angles
= |> List.indexedMap
@@ -190,6 +179,6 @@ defaults =
= , circle = "0 1"
= , center = "none"
= , radius = "0 1"
- , angles = "0 1"
+ , protractor = True
= , scatter = False
= }new file mode 100644
index 0000000..25b3173
--- /dev/null
+++ b/src/Examples/Protractor.elm
@@ -0,0 +1,41 @@
+module Examples.Protractor exposing (Config, defaults, main, ui)
+
+import Element exposing (Element)
+import Html exposing (Html)
+import Protractor
+import Svg exposing (Svg)
+import Svg.Attributes exposing (fill, stroke, viewBox)
+
+
+type alias Config =
+ { radius : Float
+ , strokeWidth : Float
+ }
+
+
+defaults : Config
+defaults =
+ { radius = 500
+ , strokeWidth = 1
+ }
+
+
+main : Html msg
+main =
+ ui defaults
+ |> Element.layout
+ [ Element.width Element.fill
+ , Element.height Element.fill
+ ]
+
+
+ui : Config -> Element msg
+ui config =
+ Protractor.protractor config
+ |> List.singleton
+ |> Svg.svg
+ [ Svg.Attributes.viewBox "-500 -500 1000 1000"
+ , Svg.Attributes.width "100%"
+ , Svg.Attributes.height "100%"
+ ]
+ |> Element.htmlindex 2e7510a..0905bcd 100644
--- a/src/Main.elm
+++ b/src/Main.elm
@@ -30,6 +30,7 @@ import Examples.Line
=import Examples.MultipleDots as MultipleDots
=import Examples.NestedTransformations
=import Examples.PolarCoordinates
+import Examples.Protractor
=import Examples.RosetteTypedTransformations
=import Examples.Spiral
=import Examples.Transformations
@@ -465,6 +466,7 @@ document =
= , dotInElement
= , dotWithViewBox
= , twoDots
+ , protractor
= , circle
= , line
= , gradient
@@ -479,6 +481,25 @@ document =
= ]
=
= -- Embeded programs' blocks
+ protractor : Mark.Block (Examples.Model -> Element Msg)
+ protractor =
+ let
+ render : Examples.Protractor.Config -> Examples.Model -> Element Msg
+ render config model =
+ Examples.Protractor.ui config
+ |> Element.el
+ [ Element.centerX
+ , Element.centerY
+ ]
+ |> Element.map Examples.CounterMsg
+ |> Element.map ExamplesMsg
+ in
+ Mark.record2 "Protractor"
+ Examples.Protractor.Config
+ (Mark.field "radius" Mark.float)
+ (Mark.field "strokeWidth" Mark.float)
+ |> Mark.map render
+
= counter : Mark.Block (Examples.Model -> Element Msg)
= counter =
= let
@@ -582,7 +603,7 @@ document =
= (Mark.field "circle" Mark.string)
= (Mark.field "center" Mark.string)
= (Mark.field "radius" Mark.string)
- (Mark.field "angles" Mark.string)
+ (Mark.field "protractor" Mark.bool)
= (Mark.field "scatter" Mark.bool)
= |> Mark.map render
=new file mode 100644
index 0000000..f990e26
--- /dev/null
+++ b/src/Protractor.elm
@@ -0,0 +1,92 @@
+module Protractor exposing (protractor)
+
+import Axis2d exposing (Axis2d)
+import Circle2d
+import Element exposing (fill)
+import Geometry.Svg
+import LineSegment2d exposing (LineSegment2d)
+import Point2d
+import Svg exposing (Svg)
+import Svg.Attributes exposing (fill, stroke, viewBox)
+
+
+type alias Config =
+ { radius : Float
+ , strokeWidth : Float
+ }
+
+
+protractor : Config -> Svg msg
+protractor config =
+ let
+ mark : Float -> Svg msg
+ mark direction =
+ if remainderBy 10 (floor direction) == 0 then
+ LineSegment2d.along (axis direction) (config.radius * 0.94) config.radius
+ |> Geometry.Svg.lineSegment2d strokeAttributes
+
+ else if remainderBy 5 (floor direction) == 0 then
+ LineSegment2d.along (axis direction) (config.radius * 0.96) config.radius
+ |> Geometry.Svg.lineSegment2d strokeAttributes
+
+ else
+ LineSegment2d.along (axis direction) (config.radius * 0.98) config.radius
+ |> Geometry.Svg.lineSegment2d strokeAttributes
+
+ axis : Float -> Axis2d
+ axis direction =
+ Axis2d.x
+ |> Axis2d.rotateAround Point2d.origin (degrees direction)
+
+ marks : List (Svg msg)
+ marks =
+ List.range 0 359
+ |> List.map toFloat
+ |> List.map mark
+
+ edge : Svg msg
+ edge =
+ Circle2d.withRadius config.radius Point2d.origin
+ |> Geometry.Svg.circle2d
+ [ Svg.Attributes.stroke "black"
+ , Svg.Attributes.fill "none"
+ ]
+
+ labels : List (Svg msg)
+ labels =
+ List.range 0 35
+ |> List.map ((*) 10)
+ |> List.map String.fromInt
+ |> List.map label
+
+ label : String -> Svg msg
+ label direction =
+ Svg.text_
+ [ Svg.Attributes.x "0"
+ , Svg.Attributes.y "0"
+ , Svg.Attributes.fill "black"
+ , Svg.Attributes.transform ("rotate (" ++ direction ++ ") translate (" ++ String.fromFloat (config.radius * 0.89) ++ ")")
+ , Svg.Attributes.fontSize <| String.fromFloat <| config.radius * 0.05
+ , Svg.Attributes.dominantBaseline "central"
+ , Svg.Attributes.textAnchor "middle"
+ ]
+ [ Svg.text direction ]
+
+ axes : List (Svg msg)
+ axes =
+ [ LineSegment2d.along Axis2d.x (config.radius * 0.84 * -1) (config.radius * 0.84)
+ , LineSegment2d.along Axis2d.y (config.radius * 0.84 * -1) (config.radius * 0.84)
+ ]
+ |> List.map (Geometry.Svg.lineSegment2d strokeAttributes)
+
+ strokeAttributes =
+ [ stroke "gray"
+ , Svg.Attributes.strokeWidth (String.fromFloat config.strokeWidth)
+ ]
+ in
+ edge
+ :: marks
+ |> List.append labels
+ |> List.append axes
+ |> Svg.g
+ []Make Dots example work, but without configurable continer
On by
You can put one or many dots, but the viewbox and background are fixed. See the FIXME comment for more details.
index 03cc7bd..5944489 100644
--- a/src/Examples/Dots.elm
+++ b/src/Examples/Dots.elm
@@ -75,5 +75,7 @@ ui { container, dots } =
= |> Svg.svg
= [ Svg.Attributes.viewBox container.viewBox
= , Svg.Attributes.style <| "background: " ++ container.background
+ , Svg.Attributes.width "100%"
+ , Svg.Attributes.height "100%"
= ]
= |> Element.htmlindex 006af8a..eae8b16 100644
--- a/src/Main.elm
+++ b/src/Main.elm
@@ -506,28 +506,6 @@ document =
=
= dots : Mark.Block (Examples.Model -> Element Msg)
= dots =
- {- FIXME: This produces parsing errors
-
- Markup:
-
- | Dots
- | Container
- background = brown
- viewBox = -200 -200 400 400
-
- | Dot
- cx = 10
- cy = 20
- color = blue
- radius = 33
-
- 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
- -}
= let
= render :
= Examples.Dots.Config
@@ -552,13 +530,48 @@ 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"
- render
- (Mark.startWith
- Examples.Dots.Config
- container
- (Mark.manyOf [ dot ])
+ (Examples.Dots.Config
+ { background = "none"
+ , viewBox = "-300 -300 600 600"
+ }
+ >> render
= )
+ (Mark.manyOf [ dot ])
=
= circle : Mark.Block (Examples.Model -> Element Msg)
= circle =Update markup to work with previously commit changes
On by
See the message of the parent commit.
index 2068cdf..c7385a8 100644
--- a/content/day-2.txt
+++ b/content/day-2.txt
@@ -26,16 +26,26 @@ Previously we have created a program that displays five colorful dots. Today we
= radius = 0 1
= scatter = False
=
-| Dots
- | Container
- background = brown
- viewBox = -200 -200 400 400
-
- | Dot
- cx = 10
- cy = 20
- color = blue
- radius = 33
+
+| 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
= What does it mean to be placed on a circle?Remove two dots example
On by
It doesnt't compile and I'm working on a more versatile Dots example.
index 5cd1456..4a1800a 100644
--- a/content/day-2.txt
+++ b/content/day-2.txt
@@ -4,7 +4,6 @@
=| Emphasize
= Let's Place the Dots in a Circle
=
-
=| Note
= Today we are going to lear about
=
@@ -15,7 +14,7 @@
=| Header
= The Problem
=
-Previously we have created a program that displays five colorful dots. Today we want to place our five dots in a circle, like this:
+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:
=
=| Window
= | Circle
@@ -26,6 +25,12 @@ Previously we have created a program that displays five colorful dots. Today we
= radius = 0 1
= scatter = False
=
+| Header
+ Multiple Dots
+
+| Note
+ TODO: Describe going from one to many dots. Most of the content is in Day 3, so just move it here and redact.
+
=| Header
= What does it mean to be placed on a circle?
=index 2e6df38..9104b65 100644
--- a/src/Main.elm
+++ b/src/Main.elm
@@ -464,7 +464,6 @@ document =
= , dot
= , dotInElement
= , dotWithViewBox
- , twoDots
= , circle
= , line
= , gradient
@@ -541,34 +540,6 @@ document =
= (Mark.field "viewBox" Mark.string)
= |> Mark.map render
=
- twoDots : Mark.Block (Examples.Model -> Element Msg)
- twoDots =
- let
- render config model =
- MultipleDots.ui config
- in
- Mark.record6 "TwoDots"
- (\background viewBoxConf firstFill secondFill firstCX secondCX ->
- MultipleDots.Config background
- viewBoxConf
- [ [ Svg.Attributes.r "10"
- , Svg.Attributes.fill firstFill
- , Svg.Attributes.cx firstCX
- ]
- , [ Svg.Attributes.r "10"
- , Svg.Attributes.fill secondFill
- , Svg.Attributes.cx secondCX
- ]
- ]
- )
- (Mark.field "background" Mark.string)
- (Mark.field "viewBox" Mark.string)
- (Mark.field "firstFill" Mark.string)
- (Mark.field "secondFill" Mark.string)
- (Mark.field "firstCX" Mark.string)
- (Mark.field "secondCX" Mark.string)
- |> Mark.map render
-
= circle : Mark.Block (Examples.Model -> Element Msg)
= circle =
= letMerge branch 'fix-9-replace-p-with-div' into 'master'
On by
Temporary fix for #9
See merge request software-garden/software-garden.gitlab.io!6
Merge branch 'protractor' into 'master'
On by
Create Protractor example and display protractor in Circle example
See merge request software-garden/software-garden.gitlab.io!7
Merge remote-tracking branch 'origin/master' into content
On by
Merge remote-tracking branch 'origin/master' into content
On by
Deploy
On by
index 4472935..8d347ad 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -13,5 +13,5 @@ pages:
= paths:
= - public
=
- only:
- - master
+ # only:
+ # - masterDraft transition from one to five dots on day 2
On by
index a809f8b..04f4e0a 100644
--- a/content/day-2.txt
+++ b/content/day-2.txt
@@ -31,6 +31,112 @@ Previously we have created a program that displays one dot in the center of the
=| Note
= TODO: Describe going from one to many dots. Most of the content is in Day 3, so just move it here and redact.
=
+ See the FIXME comment in {Code|Main.elm} on {Code|wip-dots-example}.
+
+First obvious difference is that now we have multiple dots. The only dot we have is created using {Code|Svg.circle} element on line 9 - 15, this code block:
+
+| Monospace
+ Svg.circle
+ [ Svg.Attributes.r "10"
+ , Svg.Attributes.cx "0"
+ , Svg.Attributes.cy "0"
+ , Svg.Attributes.fill "skyblue"
+ ]
+ []
+
+If you look closely in your source code, the block above is surrounded by {Code|[} and {Code|]} 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:
+
+| Code
+ module Main exposing (main)
+
+ import Element
+ import Svg
+ import Svg.Attributes
+
+
+ main =
+ [ Svg.circle
+ [ Svg.Attributes.r "10"
+ , Svg.Attributes.cx "0"
+ , Svg.Attributes.cy "0"
+ , Svg.Attributes.fill "skyblue"
+ ]
+ []
+ , Svg.circle
+ [ Svg.Attributes.r "10"
+ , Svg.Attributes.cx "0"
+ , Svg.Attributes.cy "0"
+ , Svg.Attributes.fill "orange"
+ ]
+ []
+ ]
+ |> Svg.svg
+ [ Svg.Attributes.height "100%"
+ , Svg.Attributes.width "100%"
+ , Svg.Attributes.viewBox "-300 -300 600 600"
+ ]
+ |> Element.html
+ |> Element.layout
+ [ Element.width Element.fill
+ , Element.height Element.fill
+ ]
+
+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
+
+Oh oh. There we can see the orange dot, but where is the blue one? It's behind the orange. Recall the cartesian coordinates. If we want to see both dots, let's move one of them to a different place, like this:
+
+| Code
+ module Main exposing (main)
+
+ import Element
+ import Svg
+ import Svg.Attributes
+
+
+ main =
+ [ Svg.circle
+ [ Svg.Attributes.r "10"
+ , Svg.Attributes.cx "0"
+ , Svg.Attributes.cy "0"
+ , Svg.Attributes.fill "skyblue"
+ ]
+ []
+ , Svg.circle
+ [ Svg.Attributes.r "10"
+ , Svg.Attributes.cx "50"
+ , Svg.Attributes.cy "0"
+ , Svg.Attributes.fill "orange"
+ ]
+ []
+ ]
+ |> Svg.svg
+ [ Svg.Attributes.height "100%"
+ , Svg.Attributes.width "100%"
+ , Svg.Attributes.viewBox "-300 -300 600 600"
+ ]
+ |> Element.html
+ |> Element.layout
+ [ Element.width Element.fill
+ , Element.height Element.fill
+ ]
+
+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.
+
=| Header
= What does it mean to be placed on a circle?
=
@@ -82,13 +188,10 @@ We can say that four or more dots lay on a circle, if the distance to the center
=
= TODO: An example showing that for three different points there will always be a circle that crosses them, see {Link|circumcircle|url=https://package.elm-lang.org/packages/ianmackenzie/elm-geometry/latest/Triangle2d#circumcircle}
=
-
-Because we are all about challenges, let's make five dots. Also, we already have five dot's we created on {Link|Day 1|url=/day-1.html}, so why not reuse them?
-
=So, once again - we can say that several things (in our case dots) lay on a circle if the distance between each of them and the center of the circle is the same.
=
=| Note
- A matematician would say:
+ 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./
=
@@ -101,7 +204,7 @@ We call this distance /a *radius* of a circle/. We already saw radius used as an
= [ Svg.Attributes.r "10"]
= []
=
-The {Code|r} attribute stands for radius. Then it was basically dictating the size of the dot. The larger the radius the bigger the circle, and a dot is just a filled circle!
+The {Code|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!
=
=This time the radius will affect the size of an imaginary circle on which the dots are laying.
=Make the Circle example take a radius parameter
On by
It changes the distance of the dots from the center of the circle.
Old parameter radius (controlling the visibility of the lines connecting the dots and center) is renamed to radi.
Also edit day 2.
index 04f4e0a..af026fb 100644
--- a/content/day-2.txt
+++ b/content/day-2.txt
@@ -22,18 +22,17 @@ Previously we have created a program that displays one dot in the center of the
= circle = 0 1
= protractor = False
= center = none
- radius = 0 1
+ radi = 0 1
+ radius = 80
= scatter = False
=
=| Header
= Multiple Dots
=
=| Note
- TODO: Describe going from one to many dots. Most of the content is in Day 3, so just move it here and redact.
+ See the FIXME comment in {Code|Main.elm} on {Code|wip-dots-example} branch.
=
- See the FIXME comment in {Code|Main.elm} on {Code|wip-dots-example}.
-
-First obvious difference is that now we have multiple dots. The only dot we have is created using {Code|Svg.circle} element on line 9 - 15, this code block:
+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:
=
=| Monospace
= Svg.circle
@@ -44,7 +43,7 @@ First obvious difference is that now we have multiple dots. The only dot we have
= ]
= []
=
-If you look closely in your source code, the block above is surrounded by {Code|[} and {Code|]} 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:
+If you look closely in your source code, the block above is surrounded by {Code|[} and {Code|]} 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 {Code|Svg msg} - our lonely, blue dot. Let's add a second one like this:
=
=| Code
= module Main exposing (main)
@@ -81,7 +80,7 @@ If you look closely in your source code, the block above is surrounded by {Code|
= , Element.height Element.fill
= ]
=
-The result should be:
+Now the list spans from lines 9 to 23 and contains two items: skyblue and orange dots. The result should be:
=
=| Monospace
= | Window
@@ -98,7 +97,7 @@ The result should be:
= color = orange
= radius = 10
=
-Oh oh. There we can see the orange dot, but where is the blue one? It's behind the orange. Recall the cartesian coordinates. If we want to see both dots, let's move one of them to a different place, like this:
+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:
=
=| Code
= module Main exposing (main)
@@ -135,6 +134,23 @@ Oh oh. There we can see the orange dot, but where is the blue one? It's behind t
= , Element.height Element.fill
= ]
=
+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
+
=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.
=
=| Header
@@ -147,7 +163,8 @@ We all have some intuition about a circle. You can tell if you see one and if yo
= circle = 1 0
= protractor = False
= center = none
- radius = 0 1
+ radi = 0 1
+ radius = 80
= scatter = False
=
=
@@ -158,38 +175,21 @@ But what is it that makes a circle what it is? First of all we have to realize t
= circle = 1 0
= protractor = False
= center = red
- radius = 0 1
+ radi = 0 1
+ radius = 80
= scatter = False
=
-We can say that four or more dots lay on a circle, if the distance to the center is the same for each of them, like this:
+We can say that dots lay on a circle, if the distance to the center is the same for each of them, like this:
=
=| Circle
= dots = 5
= circle = 3 3
= protractor = False
= center = red
- radius = 3 3
+ radi = 3 3
+ radius = 80
= scatter = False
=
-| Note
- Why more than three? One, two or three dots always lay on at least one circle, so it's not much of a challenge that way.
-
- One dot lay on any circle that crosses the place. You could draw very many circles like that (in fact as many as you want).
-
- Similar for two things. Not every circle will do, but still you could draw many different circles that they will lay on.
-
- For three things there will always be exactly one circle that they lay on (except if they lay in one line). So three is still not much of a challenge. You could place them however you like, and always say that they lay on a circle.
-
- Only when it's four or more things, we can seriously ask whether they lay on a circle or not.
-
- TODO: Consider if the above adds more value or confusion.
-
- TODO: An example showing multiple circles crossing one and two points
-
- TODO: An example showing that for three different points there will always be a circle that crosses them, see {Link|circumcircle|url=https://package.elm-lang.org/packages/ianmackenzie/elm-geometry/latest/Triangle2d#circumcircle}
-
-So, once again - we can say that several things (in our case dots) lay on a circle if the distance between each of them and the center of the circle is the same.
-
=| Note
= A mathematician would say:
=
@@ -206,8 +206,90 @@ We call this distance /a *radius* of a circle/. We already saw radius used as an
=
=The {Code|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!
=
+| Row
+ | Monospace
+ radius = 40
+
+ | Monospace
+ radius = 60
+
+ | Monospace
+ 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
+
+ | Monospace
+ TODO: Dots example
+ | Dots
+ | Container
+ viewBox = -100 -100 200 200
+ background = none
+ fill = True
+
+ | Dot
+ radius = 60
+ 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
+
=This time the radius will affect the size of an imaginary circle on which the dots are laying.
=
+| Row
+ | Circle
+ dots = 5
+ circle = 3 3
+ protractor = False
+ center = none
+ radi = 0 1
+ radius = 40
+ scatter = False
+
+ | Circle
+ dots = 5
+ circle = 3 3
+ protractor = False
+ center = none
+ radi = 0 1
+ radius = 60
+ scatter = False
+
+
+ | Circle
+ dots = 5
+ circle = 3 3
+ protractor = False
+ center = none
+ radi = 0 1
+ radius = 80
+ scatter = False
+
=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 {Code|(0, 0)}) and radius (any length you like, say 80).
@@ -229,7 +311,8 @@ Now, notice that the dots displayed by the program we are creating are not layin
= circle = 0 1
= protractor = False
= center = none
- radius = 0 1
+ radi = 0 1
+ radius = 80
= scatter = True
=
= | Circle
@@ -237,7 +320,8 @@ Now, notice that the dots displayed by the program we are creating are not layin
= circle = 0 1
= protractor = False
= center = none
- radius = 0 1
+ radi = 0 1
+ radius = 80
= scatter = False
=
=On the right, you immediately see a patern. They form a circle. But in fact both pictures are showing 5 dots laying on a circle. Look:
@@ -248,7 +332,8 @@ On the right, you immediately see a patern. They form a circle. But in fact both
= circle = 3
= protractor = False
= center = none
- radius = 0 1
+ radi = 0 1
+ radius = 80
= scatter = True
=
= | Circle
@@ -256,7 +341,8 @@ On the right, you immediately see a patern. They form a circle. But in fact both
= circle = 3
= protractor = False
= center = none
- radius = 0 1
+ radi = 0 1
+ radius = 80
= scatter = False
=
=
@@ -319,7 +405,8 @@ Turns out that the result is 72. That's the number of degrees we have to turn ou
= circle = 3 3
= protractor = True
= center = red
- radius = 3 3
+ radi = 3 3
+ radius = 80
= scatter = False
=
=| Header
@@ -333,7 +420,8 @@ Now that we understand what it means to be placed on a circle and evenly distrib
= circle = 0 1
= protractor = False
= center = none
- radius = 0 1
+ radi = 0 1
+ radius = 80
= scatter = False
=
=We will start from where we left off yesterday. Open {Code|src//Main.elm} in your editor. It should have the following code:
@@ -399,7 +487,8 @@ and {Link|open the program in your browser|url=http://localhost/src/Main.elm}. Y
= circle = 0 1
= protractor = False
= center = none
- radius = 0 1
+ radi = 0 1
+ radius = 80
= scatter = False
=
=As you can see the dot is no longer in the center - it has moved!
@@ -553,7 +642,8 @@ In the browser it should look exactly as we planned:
= circle = 0 1
= protractor = False
= center = none
- radius = 0 1
+ radi = 0 1
+ radius = 80
= scatter = False
=
=| Emphasizeindex c5d3b90..6e99a6f 100644
--- a/src/Examples/Circle.elm
+++ b/src/Examples/Circle.elm
@@ -18,17 +18,28 @@ type alias Config =
= { dots : Int -- Number of dots
= , circle : String -- Dash array
= , center : String -- Color
- , radius : String -- Dash array
+ , radius : Float -- The radius of the big circle
+ , radi : String -- Dash array
= , protractor : Bool -- Display protractor?
= , scatter : Bool -- Are doot evenly distributed or scattered
= }
=
=
+defaults : Config
+defaults =
+ { dots = 5
+ , circle = "0 1"
+ , center = "none"
+ , radius = 80
+ , radi = "0 1"
+ , protractor = True
+ , scatter = False
+ }
+
+
=
={- TODO:
= - Display radius (text)
- - Display angles of turns (in degrees)
- - Make an arrow head at the end point of the arc representing a turn (see https://package.elm-lang.org/packages/ianmackenzie/elm-geometry/latest/Arc2d#tangentDirection)
=-}
=
=
@@ -69,7 +80,10 @@ ui config =
= protractor =
= case config.protractor of
= True ->
- Protractor.protractor { radius = 60, strokeWidth = 0.5 }
+ Protractor.protractor
+ { radius = config.radius * 0.6
+ , strokeWidth = 0.5
+ }
=
= False ->
= Svg.g
@@ -105,7 +119,7 @@ ui config =
=
= circle =
= Svg.circle
- [ Svg.Attributes.r "80"
+ [ Svg.Attributes.r <| String.fromFloat config.radius
= , Svg.Attributes.stroke "silver"
= , Svg.Attributes.strokeWidth "1"
= , Svg.Attributes.strokeDasharray config.circle
@@ -143,7 +157,7 @@ ui config =
= , Svg.Attributes.transform <|
= Transformations.apply
= [ Rotate angle
- , Translate 80 0
+ , Translate config.radius 0
= ]
= ]
= []
@@ -159,10 +173,10 @@ ui config =
= Svg.line
= [ Svg.Attributes.x1 "0"
= , Svg.Attributes.y1 "0"
- , Svg.Attributes.x2 "80"
+ , Svg.Attributes.x2 <| String.fromFloat config.radius
= , Svg.Attributes.y2 "0"
= , Svg.Attributes.stroke "pink"
- , Svg.Attributes.strokeDasharray config.radius
+ , Svg.Attributes.strokeDasharray config.radi
= , Svg.Attributes.transform <|
= Transformations.apply
= [ Rotate angle
@@ -175,14 +189,3 @@ ui config =
= [ Svg.Attributes.viewBox "-100 -100 200 200"
= ]
= |> Element.html
-
-
-defaults : Config
-defaults =
- { dots = 5
- , circle = "0 1"
- , center = "none"
- , radius = "0 1"
- , protractor = True
- , scatter = False
- }index a02ca19..b92c418 100644
--- a/src/Main.elm
+++ b/src/Main.elm
@@ -442,24 +442,33 @@ document =
= }
= )
= Mark.Custom.title
- (Mark.manyOf
- ([ Mark.Custom.header
- , Mark.Custom.paragraph
- , Mark.Custom.monospace
- , Mark.Custom.code
- , Mark.Custom.terminal
- , Mark.Custom.note
- , Mark.Custom.emphasize
- , Mark.Custom.list
- , Mark.Custom.image
- ]
- ++ examples
- ++ [ Mark.Custom.window (Mark.oneOf (Mark.Custom.paragraph :: examples))
- , Mark.Custom.row (Mark.manyOf examples)
- ]
- )
+ ([ typography, widgets, examples, special ]
+ |> List.concat
+ |> Mark.manyOf
= )
=
+ typography =
+ [ Mark.Custom.header
+ , Mark.Custom.paragraph
+ , Mark.Custom.monospace
+ , Mark.Custom.emphasize
+ , Mark.Custom.list
+ , Mark.Custom.image
+ ]
+
+ widgets =
+ [ Mark.Custom.code
+ , Mark.Custom.terminal
+ , Mark.Custom.note
+ ]
+
+ special =
+ [ Mark.Custom.window <|
+ Mark.oneOf (typography ++ examples ++ widgets)
+ , Mark.Custom.row <|
+ Mark.manyOf (typography ++ examples ++ widgets)
+ ]
+
= examples =
= [ counter
= , dot
@@ -568,12 +577,13 @@ document =
= render config model =
= Examples.Circle.ui config
= in
- Mark.record6 "Circle"
+ Mark.record7 "Circle"
= Examples.Circle.Config
= (Mark.field "dots" Mark.int)
= (Mark.field "circle" Mark.string)
= (Mark.field "center" Mark.string)
- (Mark.field "radius" Mark.string)
+ (Mark.field "radius" Mark.float)
+ (Mark.field "radi" Mark.string)
= (Mark.field "protractor" Mark.bool)
= (Mark.field "scatter" Mark.bool)
= |> Mark.map renderEdits to day 3
On by
index 5780836..8309bf5 100644
--- a/content/day-3.txt
+++ b/content/day-3.txt
@@ -5,7 +5,7 @@
= Connecting the Dots
=
=| Note
- So far we have been writing the code of our program without thinking too much about about what it means. Today we are going to learn about
+ So far we have been writing the code of our program without thinking too much about what it means. Today we are going to learn about
=
= | List
= - Values and names
@@ -48,7 +48,7 @@ Let's start with values. The simplest way to create a value is to literally type
=
=These expressions ({Code|2} and {Code|"Hello!"}) are called /literals/, because their values are literally what they look like. The value of {Code|2} is simply {Code|2}! When you enter them, the REPL evaluates and prints their value (which in this case is very easy) and type (the text after {Code|:}). We will get to types later.
=
-Some value literals are simple, like numbers and strings. On day 1 we discussed lists. You can also type them literally, like this:
+Some value literals are simple, like numbers and strings. Some are complex. Previously we have touched the subject of lists. You can also type them literally, like this:
=
=| Terminal
= > [ 1, 2, 5, 77, 2 ]
@@ -72,9 +72,9 @@ or contain only one element:
= ["Hello"] : List String
=
=| Note
- Notice that a list with one element is not the same as the element itself, just like the box with one egg is not the same as an egg alone.
+ Notice that a list with one element is not the same as the element itself, just like the box with one egg is not the same as an egg alone. Also an empty list is not nothing, just like an empty box of eggs is not nothing. It may be a major disappointment when you are about to make some pancakes, but it's still a thing.
=
-Now let's focus our attention to *names*. You can give any value a name.
+Now let's focus our attention to *names*. If you have a value, you can give it a name.
=
=| Terminal
= > kittens = 2
@@ -91,13 +91,13 @@ From now on you can refer to this value either literally or by it's name:
= > 2
= 2 : number
=
-The effect will be exactly the same - you will get a value back. So we see that there are at least two ways of getting values: by literally expressing them or refering to them by names given to them previously. In a composite value of a list, you can mix the two ways together:
+The effect will be exactly the same - you will get a value back. So we see that there are at least two ways of getting values: by literally expressing them or referring to them by names given to them previously. In a composite value of a list, you can mix the two ways together:
=
=| Terminal
= > [ 1, kittens, 3 ]
= [1,2,3] : List number
=
-A function is a special kind of a value. Let's create a named function like this:
+A *function* is a special kind of a value. Let's create a named function like this:
=
=| Terminal
= > fun something = something ++ " is fun!"
@@ -114,12 +114,14 @@ Above we applied the function (by calling it's name: {Code|fun}) to a literal va
=| Note
= Imagine a machine that paints eggs. You put an egg into the machine and on the other side you get a painted egg. The egg is a thing and the painted egg is also a thing. But so is the machine!
=
+ Alternatively you can think of functions as parametrized values. The value you get depends on what value you provide to it. Eggs can have different sizes . An egg painting machine would give you different painted eggs depending on what egg you put in it.
+
=Now we see that we can get a value in three ways: (1) by literally expressing it, (2) by calling it's name and (3) by applying a function to another value.
=
-Not every function can be applied to any value. You can add two numbers (like {Code|2} and {Code|5} in {Code|2 + 5}), but you can't a number to a string ({Code|2 + "Hello!"} - try it and see an error!). The system that governs what function can be applied to what value is called a *type system* and it plays important role in Elm programming.
+Not every function can be applied to any value. You can add two numbers (like {Code|2} and {Code|5} in {Code|2 + 5}), but you can't add a number to a string ({Code|2 + "Hello!"} - try it and see an error!). The system that governs what function can be applied to what value is called a *type system* and it plays important role in Elm programming.
=
=| Note
- Again, imagine a machine that paints eggs. If you try to put a carrot in that machine, the dumb machine would make a mess of your carrot and probably jam. But a smart machine would sound a low pitch buzz and simply refuse to take the carrot. That way a smart machine avoids making a mess and possibly damaging itself. The Elm's type system is what makes your functions smart. It stops you from putting a carrot into the egg painting machine!
+ Again, imagine a machine that paints eggs. If you try to put a carrot in that machine, the dumb machine would make a mess of your carrot and probably get jammed. But a smart machine would sound a low pitched buzz and simply refuse to take the carrot . That way a smart machine avoids making a mess and possibly damaging itself. The Elm's type system is what makes your functions smart. It prevents you from putting a carrot into the egg painting machine!
=
=Every value in Elm has a type. You can see the type in the REPL - after evaluating the expression it shows it's value and the type, like here:
=
@@ -141,7 +143,7 @@ Composite values like lists have composite types. For example here the type is {
= > [ "Hello", "Good bye" ]
= ["Hello","Good bye"] : List String
=
-This means that it's a list of strings. That means that all the elements of this list are of type {Code|String}. When talking about lists you have to tell that it's a list and what is the type of it's element. Only this can be considered a fully specified type. Compare:
+This means that it's a list of strings. All the elements of this list are of type {Code|String}. When talking about lists you have to tell that it's a list and what is the type of it's element. Only this can be considered a fully specified type. Compare:
=
=| Terminal
= > [ 1, 2, 3 ]
@@ -150,13 +152,13 @@ This means that it's a list of strings. That means that all the elements of this
=This is a list of numbers. It's easy to see - all the elements are numbers, just as previously they were strings. All the elements of a list must be of the same type. If you try mixing the types, Elm will refuse to build your program. Try:
=
=| Terminal
- > [ 1, 2, 3, "Hello" ]
+ > [ 1, 2, 3, "Carrot" ]
= -- TYPE MISMATCH ----------------------------------------------------------- elm
=
= The 4th element of this list does not match all the previous elements:
=
- 7| [ 1, 2, 3, "Hello" ]
- ^^^^^^^
+ 7| [ 1, 2, 3, "Carrot" ]
+ ^^^^^^^^
= The 4th element is a string of type:
=
= String
@@ -180,7 +182,7 @@ What's the type of an empty list? Let's see:
=A list of {Code|a}. Obviously {Code|a} is not a type. This means that the type of the elements cannot be determine yet. This is signaled by the lowercase term where the type should be: {Code|a} instead of the capitalized {Code|String}.
=
=| Note
- Notice that the {Code|number} (type of value {Code|15} for example} is also lowercase. That's because it's also not a concrete type! Elm has two concrete types for numbers: {Code|Int} that can represent an integer number (1, 2, 3, 0, -122 etc.) and {Code|Float} that can represent a number with a fraction, (like 1.2, -3.5, 44.2). Because fraction part can be 0 (e.g. 1.0, 2.0, 0.0, -122.0 - so called whole numbers), it's not possible to tell if {Code|2} is an {Code|Int} or a {Code|Float}. We will see some of the implications of this later.
+ Notice that the {Code|number} (type of value {Code|15} for example) is also lowercase. That's because it's also not a concrete type! Elm has two concrete types for numbers: {Code|Int} that can represent an integer number (1, 2, 3, 0, -122 etc.) and {Code|Float} that can represent a number with a fraction, (like 1.2, -3.5, 44.2). Because fraction part can be 0 (e.g. 1.0, 2.0, 0.0, -122.0 - so called whole numbers), it's not possible to tell if {Code|2} is an {Code|Int} or a {Code|Float}. We will see some of the implications of this later.
=
=Above I said that every value has a type and also that a function is a value on it's own. Let's try the following in REPL:
=
@@ -188,7 +190,7 @@ Above I said that every value has a type and also that a function is a value on
= > fun
= <function> : String -> String
=
-We have asked the REPL to evaluate the value referenced by name {Code|fun}. In response the value is display as {Code|<function>}. That's because there is no simple way to represent the value of a function as text. Let's focus our attention on the type (part after the colon {Code|:}). It is {Code|String -> String}. This means that the type is a function (we recognize it by the arrow symbol). What's before the arrow is a type of the value that goes into the function (called the /argument of the function/). The type of the argument is {Code|String}. That shouldn't be a surprise. Remember how we have used the function before to produce the value:
+We have asked the REPL to tell as the value referenced by name {Code|fun}. In response the REPL display the value as {Code|<function>}. That's because there is no simple way to represent the value of a function as text. Let's focus our attention on the type (part after the colon {Code|:}). It is {Code|String -> String}. This means that the type is a function (we recognize it by the arrow symbol). What's before the arrow is a type of the value that goes into the function (called the /argument of the function/). The type of the argument is {Code|String}. That shouldn't be a surprise. Remember how we have used the function before to produce the value:
=
=| Terminal
= > fun "Learning Elm"
@@ -198,7 +200,7 @@ The part expressed as {Code|"Learning Elm"} is a {Code|String} (we recognize it
=
=The part after the arrow symbol is the type that the function returns. It is also {Code|String}. That also should not surprise us, since {Code|"Learning Elm is fun!"} is also a {Code|String}.
=
-Not every function returns the same type as it takes. For example function {Code|String.fromInt} takes a value of type {Code|Int} and returns a value of type {Code|String}. It's type is therefor {Code|Int -> String}. We read it as: /type of String.fromInt is a function from Int to String/. Let's see it in action:
+Not every function returns the same type as it takes. For example the function {Code|String.fromInt} takes a value of type {Code|Int} and returns a value of type {Code|String}. It's type is therefor {Code|Int -> String}. We read it as: /type of String.fromInt is a function from Int to String/. Let's see it in action:
=
=| Terminal
= > String.fromInt 10
@@ -218,7 +220,7 @@ But what if we want to say that a certain number (say, {Code|232}) is fun? We ha
=
=We had to put the {Code|String.fromInt 232} expression in parentheses, so that Elm understands that value {Code|232} is to be passed to {Code|String.fromInt} function, instead of the {Code|String.fromInt} being passed to {Code|fun} (which would not work anyway, since {Code|fun} takes values of type {Code|String}, not {Code|Int -> String}).
=
-Alternatively we can use the pipe operator which looks like this: {Code|\|>}. It has the advantage that the code that the transformations that happen first are on the left side and ones that happen later on the right, so we can read our code left to right instead of inside out. Here is how it would look like:
+Alternatively we can use the pipe operator which looks like this: {Code|\|>}. It has the advantage that the transformations that happen first are on the left side and ones that happen later on the right. We can read our code left to right instead of inside out. Here is how it would look like:
=
=| Terminal
= > 232 |> String.fromInt |> fun
@@ -239,6 +241,70 @@ Now it's more apparent that the value {Code|232} is passed to the function {Code
=
= Stay tuned {Icon|name=radio}
=
+Let's look at our code and try to identify some literal values, names and functions:
+
+| Code
+ module Main exposing (main)
+
+ import Element
+ import Svg
+ import Svg.Attributes
+
+
+ main =
+ Svg.svg
+ [ Svg.Attributes.viewBox "-100 -100 200 200"
+ ]
+ [ Svg.circle
+ [ Svg.Attributes.r "10"
+ , Svg.Attributes.cx "0"
+ , Svg.Attributes.cy "0"
+ , Svg.Attributes.transform "translate(80)"
+ , Svg.Attributes.fill "skyblue"
+ ]
+ []
+ , Svg.circle
+ [ Svg.Attributes.r "10"
+ , Svg.Attributes.cx "0"
+ , Svg.Attributes.cy "0"
+ , Svg.Attributes.fill "orange"
+ , Svg.Attributes.transform "rotate(72) translate(80)"
+ ]
+ []
+ , Svg.circle
+ [ Svg.Attributes.r "10"
+ , Svg.Attributes.cx "0"
+ , Svg.Attributes.cy "0"
+ , Svg.Attributes.fill "red"
+ , Svg.Attributes.transform "rotate(144) translate(80)"
+ ]
+ []
+ , Svg.circle
+ [ Svg.Attributes.r "10"
+ , Svg.Attributes.cx "0"
+ , Svg.Attributes.cy "0"
+ , Svg.Attributes.fill "lime"
+ , Svg.Attributes.transform "rotate(216) translate(80)"
+ ]
+ []
+ , Svg.circle
+ [ Svg.Attributes.r "10"
+ , Svg.Attributes.cx "0"
+ , Svg.Attributes.cy "0"
+ , Svg.Attributes.fill "maroon"
+ , Svg.Attributes.transform "rotate(288) translate(80)"
+ ]
+ []
+ ]
+ |> Element.html
+ |> Element.layout
+ [ Element.width Element.fill
+ , Element.height Element.fill
+ ]
+
+Then let's consider where we have a repetitive code that we could turn into a named function.
+
+
=Let's take a deeper look at how we give attributes to an SVG element.
=
=We've already seen a few example of SVG attributes. The radius of our dot ({Code|circle}) is an attribute.Draft the rest of the syntax section
On by
index 8309bf5..67e6def 100644
--- a/content/day-3.txt
+++ b/content/day-3.txt
@@ -228,20 +228,28 @@ Alternatively we can use the pipe operator which looks like this: {Code|\|>}. It
=
=Now it's more apparent that the value {Code|232} is passed to the function {Code|String.fromInt}, and whatever comes on the other end is passed to the {Code|fun} function. We don't need parentheses in this case.
=
-| Note
- TODO: But where does the functions come from? Modules.
-
+But what are functions good for? They are very good for making similar values that depend on some parameters. If you have to repeat similar code many times to get similar, but slightly different results, try using a function.
=
+| Note
+ Just like machines for painting eggs are useful if you have a lot of eggs to paint. If you only need one or two painted eggs, then it's probably better to just get them painted. But if you have thousands, maybe it's worth to build a machine for it.
=
+We don't have to create all the functions that we need. There are thousands of them already defined and we can use them by importing modules. We already did that. First you import a module like this:
=
+| Terminal
+ > import String
=
+And then use any name that it exposes, like this
=
=| Note
= *We are still working on this content.*
+| Terminal
+ > String.fromInt 10
+ "10" : String
=
= Stay tuned {Icon|name=radio}
+If the name comes from an imported module, you need to prefix it with the name of the module, like we did above.
=
-Let's look at our code and try to identify some literal values, names and functions:
+Let's look at our code and try to recognize some literal values, lists, names and functions. Here is how it should look after day 2:
=
=| Code
= module Main exposing (main)
@@ -302,7 +310,24 @@ Let's look at our code and try to identify some literal values, names and functi
= , Element.height Element.fill
= ]
=
-Then let's consider where we have a repetitive code that we could turn into a named function.
+Take your time. Here are some hints to help you:
+
+| List
+ #. We import three modules. Their names are to the right of the {Code|import} keyword.
+
+ #. Literal values are numbers and quoted texts. In our code there are only string literals so try to find all texts in quotes ({Code|"like this"}). I<>count 26 of them.
+
+ #. Then there are 13 lists, 5 of them empty and 8 with elements. Some lists are nested in other lists!
+
+ #. Names are given to values wherever you see a {Code|=} sign. The name is on the left and the value is below and slightly to the right. In our code we are giving only one name and it's value is quite complex. In fact the whole program is about computing this value. Try to find it.
+
+ #. Functions are called wherever you see a name that have any expression to the right (or below and right) of it (like: {Code|fun "learning Elm together"}), or that stand to the left of the {Code|\|>} (pipe operator; like: {Code|"learning Elm together" \|> fun}). The expression to the right of the function (or left of pipe) is called /the argument/. Sometimes an argument is a complex expression. Some functions take more than one argument (e.g. {Code|Svg.svg} takes 2 and the second one is very complex), and sometimes one is on the right and the other comes through the pipe 🤕. There is a lot of functions called here (36 to be precise) and most are prefixed with a name of the module that exposed them to us.
+
+ #. Interestingly there are only two names exposed by a module that do not refer to a function. Try to find them!
+| Note
+ *We are still working on this content.*
+
+ Stay tuned {Icon|name=radio}
=
=
=Let's take a deeper look at how we give attributes to an SVG element.