Commits: 3
Edits to the day-1 text
Modify simplest embedded example to take background and fill config
index 3cd7b9e..83cc9bf 100644
--- a/content/day-1.txt
+++ b/content/day-1.txt
@@ -10,40 +10,40 @@
=
= | List
= -> Scalable Vector Graphics
- -> cartesian Coordinates System
+ -> Cartesian Coordinates Systems
= -> Layouts with ELM UI
=
=
=| Header
- First Program!
+ Our First Program!
=
=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. Lets call it fpart.
-In the terminal type
+In the terminal type:
=
=| Monospace
= mkdir fpart/
=
-and then
+and then:
=
=| Monospace
= cd fpart/
=
-This creates a new directory and makes it the current one. Again, don't worry about the details.
+The first command will create our new directory and the second will make it the current directory. Again, don't worry about the details.
=
-To easily create a new program, we can type
+We can easily create a new program by typing:
=
=| Monospace
= elm init
=
-Then to create a file with source code, type
+Then to create the file which will contain our source code, type:
=
=| Monospace
= atom src/Main.elm
=
-This command should open a new Atom window with empty text file.
+This command should open a new Atom window with an empty text file.
=
=| Header
= Main.elm
@@ -58,19 +58,19 @@ This command should open a new Atom window with empty text file.
= Html.text "Hello, Tree"
=
=Type the above in the editor and save the file.
-To see the program running type the following in the terminal
+To run the program, type the following command in the terminal:
=
=| Monospace
= elm reactor
=
-This command starts the Elm reactor, which will let you run your program in the web browser. Note that it won't give you the command line back - it will run as long as you don't stop it.
+This command will start the Elm reactor, which will allow you to run your program in the web browser. Note that it won't give you the command line back - it will run as long as you don't stop it.
=
=
=| Emphasize
= Voila!
=
=
-Open following address in the web browser
+Open following address in the web browser:
=
=| Emphasize
= {Link|http://localhost:8000/src/Main.elm|url=http://localhost:8000/src/Main.elm}
@@ -78,16 +78,16 @@ Open following address in the web browser
=| Note
= *TODO*: Make the link display // characters
=
-Note that the same address was printed by Elm reactor
+Note that the same address was printed by the Elm reactor.
=
=| Header
= The problem
=
-We want to have a dot at the center of the screen, like this
+We want to display a dot at the center of the screen, like this:
=
=| DotAtTheCenterOfTheScreen
=
-Below is the complete code to draw a dot like this, but don't type it in your editor yet! We are going to recreate together it step by step.
+Below is the complete code to draw a dot like this, but don't type it in your editor yet! We are going to recreate it together, step by step.
=
=| Code
= module Main exposing (main)
@@ -114,113 +114,124 @@ Below is the complete code to draw a dot like this, but don't type it in your ed
= , Element.height Element.fill
= ]
=
+We are going to use a technology called SVG (Scalable Vector Graphics).
=
+*Scalable icon should be here*
+
+S for Scalable
=
-We are going to use a technology called SVG (Scalable Vector Graphics). Its all about drawing shapes on the screen. Let's install an Elm Package to help us work with SVG. Stop the reactor running in terminal by pressing {Code|CTRL} + {Code|C} and type the following:
+Its all about drawing shapes on the screen. Let's install an Elm Package to help us work with SVG. Stop the reactor running in the terminal by pressing {Code|CTRL} + {Code|C} and then type the following:
=
=| Monospace
= elm install elm/svg
=
-Then start the reactor again:
+Now that we have {Code|elm//svg} installed, we can write the code that will draw our dot:
+
+| Code
+ module Simplest exposing (main)
+
+ import Element
+ import Svg
+ import Svg.Attributes
+
+
+ main =
+ Svg.svg []
+ [ Svg.circle
+ [ Svg.Attributes.r "10"
+ ]
+ []
+ ]
+
+Start the reactor again:
=
=| Monospace
= elm reactor
=
=| Note
- You can press up arrow {Icon|name=arrow-up} on the keyboard to get to the previous commands.
+ You can press the up arrow {Icon|name=arrow-up} on the keyboard to retrieve commands entered previously.
=
=Reload the browser. You should see something like this:
=
=| Simplest
+ background=none
+ fill=black
=
-Why is the dot at the corner?
+Something isn't quite right here. Why is the dot in the corner?
=
-It's because its center is at point called {Code|origin} or {Code|(0, 0)}. But what does it mean ?
+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.
=
-* a picture of a vase on a table should be here *
+Second, it's because the SVG space doesn't fill the browser window. Don't believe me? We can see that the space doesn't fill the window by changing the background color of the SVG element.
=
-Move sliders to change the x and y coordinates of the dot. It's like moving a thing on a table by telling how far left, right, up or down it should go.
-
-| CartesianCoordinates
+| Note
+ Code sample for simplest with pink background should be here.
=
-To change where the dot is we can use cx and cy attributes of the circle
+When you reload the browser, you'll see this:
=
-* simplest with cx and cy should be here *
+| Note
+ Example of simplest with pink background should be here.
=
-* Module main exposing main should be here *
+| Simplest
+ background=pink
+ fill=black
=
-Note that we have a complete program that draws a dot at the center of the screen , lets take a moment to understand it.
+Now we can clearly see that our SVG element (which is pink) does not fill the screen.
=
-Think about this ( * a picture of eggs and a box of eggs should be here * )
+We can easily correct this with the help of a very handy elm package, {Code|mdgriffith//elm-ui}.
=
-| Note
- An egg is a thing.
- Six eggs are six things.
- A box of six eggs is a thing.
+Install it with the terminal.
=
+Press {Code|CTRL + C} to stop the elm-reactor, type {Code|elm install mdgriffith//elm-ui} and press {Code|Enter}.
=
+Then in {Code|src//Main.elm} add the necessary import {Code|import Element} and change {Code|Main} to look like this:
=
-Now, let's make the SVG element fill the screen
+Main = * the code of Element.layout should be here*
=
+Take a wild guess: what do you think {Code|Element.height Element.fill} and {Code|Element.height Element.width} do?
=
-| FillTheScreen
+Reload the browser to confirm your hypothesis. Voila! The SVG space now fills the screen. It should now be possible to place our dot in the center of the screen. But, how exactly will we achieve it?
=
+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 eactly does this mean?
=
-For that we will need a package called {Link|mdgriffith//elm-ui|url=https://package.elm-lang.org/packages/mdgriffith/elm-ui/latest/}
+* a picture of a vase on a table should be here *
=
-Install it using terminal
+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.
=
-{Code|CTRL + C } to stop elm-reactor and type {Code|elm install mdgriffith//elm-ui}
+| CartesianCoordinates
=
-Then in {Code|src//Main.elm} add import {Code|import Element} and change {Code|Main} to look like this :
+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.
=
-Main = * the code of Element.layout should be here*
+To change the position of the dot, we can use the {Code|cx} and {Code|cy} attributes of the {Code|circle} element.
=
-Note that our scene fills the screen, it's time to put the dot at the center of the scene.
+* simplest with cx and cy should be here *
=
-* a picture of cx and cy with width and height should be here *
+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.
=
-If we would know the height and width of the screen , we could calculate its position as
+We can calculate the position of the circle as:
=
=| Monospace
= cx = width/2
= cy = height/2
=
-There is a problem though, we don't know the height and width.
+There is a problem though! We don't actually know the width and height of the SVG space. All we know is that its {Code|width} and {Code|height} will {Code|fill} the viewport, whatever its size.
=☹️
=
-But we don't need to!
+Fortunately, we don't need to know the width and height of the SVG space!
=
-*Scalable icon should be here*
+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}.
=
-S for Scalable
+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.
=
-Instead of moving the dot, we can set the boundaries of the scene so that the dot is at the center using a property called Viewbox.
+A videwbox is like a window into an SVG space. It allows us to change the point of view of the scene.
=
=| ViewBox
=
-Here is how the viewbox works
-
-* a picture of viewbox should be here*
-
-viewbox = 0 0 100 100 (left, top, width and height respectively)
-
-* a picture of periscope looking table should be here*
+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 1000.
=
-The trick is to set the width and height to any arbitrary value (say 1000) and top and left to {Code|- width//2}!
+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 500.
=
-That way point (0,0) will always be right in the middle (the distance to the left and to the right is the same, likewise the distance to the top and bottom).
+Refresh the broswer. We should now see the dot in the center of the screen.
=
-First, lets see where the SVG boundaries are by giving it a background color
-
-
-| CenteredDot
-
-
-| Code
- Svg [background "pink"]
- [...
- ]
=
=Give a color to the dot
=index d542d78..019ba58 100644
--- a/src/Main.elm
+++ b/src/Main.elm
@@ -487,16 +487,23 @@ document =
= simplest : Mark.Block (Model -> Element Msg)
= simplest =
= let
- render model =
- Simplest.main
+ render : Simplest.Config -> Model -> Element Msg
+ render config model =
+ Simplest.ui config
= |> Element.html
+ |> Element.el []
= |> Element.el
= [ Element.height (Element.px 400)
= , Element.width Element.fill
+ , Element.padding 5
= ]
= |> BrowserWindow.window []
= in
- Mark.stub "Simplest" render
+ Mark.record2 "Simplest"
+ Simplest.Config
+ (Mark.field "background" Mark.string)
+ (Mark.field "fill" Mark.string)
+ |> Mark.map render
=
= fillTheScreen : Mark.Block (Model -> Element Msg)
= fillTheScreen =index 9f90116..2e65fa4 100644
--- a/src/Simplest.elm
+++ b/src/Simplest.elm
@@ -1,17 +1,32 @@
-module Simplest exposing (main)
+module Simplest exposing (Config, defaults, ui)
=
-import Element
+import Html exposing (Html)
=import Svg
=import Svg.Attributes
=
=
+type alias Config =
+ { background : String
+ , fill : String
+ }
+
+
+defaults : Config
+defaults =
+ Config "none" "black"
+
+
+main : Html msg
=main =
- Svg.svg
- [ Svg.Attributes.width "300"
- , Svg.Attributes.height "150"
- ]
+ ui defaults
+
+
+ui : Config -> Html msg
+ui { background, fill } =
+ Svg.svg [ Svg.Attributes.style <| "background: " ++ background ]
= [ Svg.circle
= [ Svg.Attributes.r "10"
+ , Svg.Attributes.fill fill
= ]
= []
= ]Separate day 2, add TransformationsCircle example
Expose Transformation(..) and Apply from Transformations module.
new file mode 100644
index 0000000..32d4a6c
--- /dev/null
+++ b/content/day-2.txt
@@ -0,0 +1,75 @@
+| Title
+ Day 2
+
+| Emphasize
+ Place the dots in a circle
+
+
+| Note
+ Today we are going to lear about
+
+ | List
+ - SVG transformations
+ - SVG groups (?)
+
+| Header
+ The Problem
+
+We want to place the dots in a circle, like that:
+
+| TransformationsCircle
+ dots = True
+ circle = False
+ angle = False
+ center = False
+
+As you can see, we will make the dot's have different colors, so it's more fun!
+
+How we will get there?
+
+We will need more than one dot. Let's make it 5. Then let's think what does it mean to be placed on a circle.
+
+First of all we have to realize that a circle has a center. It's a point that lays exactly in the middle:
+
+
+Then we can say that several things lay on a circle if the distance between each of them and the center is the same.
+
+In other words, you can put a thing in the center and then move it in any direction, let's say 1 meter. If you do the same to several things, you will make them lay on a circle.
+
+Of course the direction must be different each time. Otherwise the things would just stack one on top of another! We wouldn't call it a circle, right?
+
+We will change the cartesian coordinates of the dots, nothing else! But figuring out the right coordinates is a bit tricky
+
+How can we figure out the correct cartesian coordinates?
+
+| Note
+ Story about the flowerpot and the table: two ways to measure relative position, one is distance from an x and y axis (cartesian coordinates), the other is angle and distance relative to origin (polar coordinates)
+
+
+What does this have to do with a circle? Do you know the expression 'I did a 180'. Where would you be looking if you did two 180s (a '360'). We see that circles and angles are closely related!
+
+Exercise with compass and 5 objects, place objects evenly along compass. How many degrees apart are they? (observe if we multiply the result by 5, we're back to 360)
+
+We have one part of it (angle), we now need the distance. Notice they are all the same distance from the origin (the center dot) of the compass. Definition of circle: points that are an equal distance from a center. Actually, the distance doesn't matter, so long as they all have the same distance. You can have a big circle or a small circle, they're both circles
+
+Ok great, we're done! Now, does anyone know how to give an angle and distance to svg? Oh... no? We don't either... you can't. You can only give x and y values relative to the origin
+
+So we already know that angle and length are just another way of describing x and y. But we need some way of translating between the two
+
+sing a visual demonstration, if you draw a line from your object to the x axis, you have a triangle. Same if you draw a line to the y axis. You can figure out the point on the axis using sin and cos functions and multiplying the result by the length.
+
+Let's use a chart to figure out the sin and cos of our angles
+
+/It's important to get a chart, otherwise we have to use calculators which work with radians/
+
+Now we are done... we can plug in our x and y values, and presto, our dots are arranged in a circle. But we don't see most of them...
+
+Our origin is in the top left corner. This means any dots with negative x or y values are off the screen.
+
+We can shift the dots so they are on the screen, or shift the screen so that it covers the dots. We will be shifting the screen
+
+Sample viewbox program to demonstrate
+
+Create a viewbox with correct perimeters (must be more than the radius of the circle plus the radius of a dot in each direction, and width and height are circumference)
+
+{Code|svg [viewbox "-100 -100 200 200 " ] []}index 54d5902..07ece92 100644
--- a/elm.json
+++ b/elm.json
@@ -16,6 +16,7 @@
= "elm/url": "1.0.0",
= "elm-community/basics-extra": "4.0.0",
= "elm-community/list-extra": "8.1.0",
+ "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",index 019ba58..ab8ac60 100644
--- a/src/Main.elm
+++ b/src/Main.elm
@@ -41,6 +41,7 @@ import Routes exposing (Route)
=import Simplest
=import Spiral
=import Transformations
+import TransformationsCircle
=import Tree
=import Url exposing (Url)
=import ViewBox
@@ -451,6 +452,7 @@ document =
= , fillTheScreen
= , dotAtTheCenterOfTheScreen
= , centeredDot
+ , transformationsCircle
= , line
= , gradient
= , transformations
@@ -545,6 +547,27 @@ document =
= in
= Mark.stub "CenteredDot" render
=
+ transformationsCircle : Mark.Block (Model -> Element Msg)
+ transformationsCircle =
+ let
+ render : TransformationsCircle.Config -> Model -> Element Msg
+ render config model =
+ TransformationsCircle.ui config
+ |> Element.el
+ [ Element.height (Element.px 400)
+ , Element.width Element.fill
+ , Element.padding 5
+ ]
+ |> BrowserWindow.window []
+ in
+ Mark.record4 "TransformationsCircle"
+ TransformationsCircle.Config
+ (Mark.field "circle" Mark.bool)
+ (Mark.field "center" Mark.bool)
+ (Mark.field "angle" Mark.bool)
+ (Mark.field "dots" Mark.bool)
+ |> Mark.map render
+
= line : Mark.Block (Model -> Element Msg)
= line =
= letindex 295c1ef..1b3937d 100644
--- a/src/Transformations.elm
+++ b/src/Transformations.elm
@@ -1,6 +1,8 @@
=module Transformations exposing
= ( Model
= , Msg
+ , Transformation(..)
+ , apply
= , init
= , main
= , uinew file mode 100644
index 0000000..96264ff
--- /dev/null
+++ b/src/TransformationsCircle.elm
@@ -0,0 +1,135 @@
+module TransformationsCircle exposing (Config, defaults, main, ui)
+
+import Element exposing (Element)
+import Html exposing (Html)
+import Maybe.Extra as Maybe
+import Svg exposing (Svg)
+import Svg.Attributes
+import Transformations exposing (Transformation(..))
+
+
+main : Html.Html msg
+main =
+ Element.layout
+ [ Element.width Element.fill
+ , Element.height Element.fill
+ ]
+ (ui defaults)
+
+
+ui : Config -> Element msg
+ui config =
+ let
+ present : Bool -> Svg msg -> Maybe (Svg msg)
+ present flag shape =
+ if flag then
+ Just shape
+
+ else
+ Nothing
+
+ shapes =
+ dots
+ ++ Maybe.values
+ [ present config.circle circle
+ ]
+
+ dots =
+ if config.dots then
+ [ Svg.circle
+ [ Svg.Attributes.r "10"
+ , Svg.Attributes.cx "0"
+ , Svg.Attributes.cy "0"
+ , Svg.Attributes.fill "skyblue"
+ , Svg.Attributes.transform <|
+ Transformations.apply
+ [ Rotate 0
+ , Translate 80 0
+ ]
+ ]
+ []
+ , Svg.circle
+ [ Svg.Attributes.r "10"
+ , Svg.Attributes.cx "0"
+ , Svg.Attributes.cy "0"
+ , Svg.Attributes.fill "pink"
+ , Svg.Attributes.transform <|
+ Transformations.apply
+ [ Rotate 72
+ , Translate 80 0
+ ]
+ ]
+ []
+ , Svg.circle
+ [ Svg.Attributes.r "10"
+ , Svg.Attributes.cx "0"
+ , Svg.Attributes.cy "0"
+ , Svg.Attributes.fill "yellow"
+ , Svg.Attributes.transform <|
+ Transformations.apply
+ [ Rotate 144
+ , Translate 80 0
+ ]
+ ]
+ []
+ , Svg.circle
+ [ Svg.Attributes.r "10"
+ , Svg.Attributes.cx "0"
+ , Svg.Attributes.cy "0"
+ , Svg.Attributes.fill "lime"
+ , Svg.Attributes.transform <|
+ Transformations.apply
+ [ Rotate 216
+ , Translate 80 0
+ ]
+ ]
+ []
+ , Svg.circle
+ [ Svg.Attributes.r "10"
+ , Svg.Attributes.cx "0"
+ , Svg.Attributes.cy "0"
+ , Svg.Attributes.fill "maroon"
+ , Svg.Attributes.transform <|
+ Transformations.apply
+ [ Rotate 288
+ , Translate 80 0
+ ]
+ ]
+ []
+ ]
+
+ else
+ []
+
+ circle =
+ Svg.circle
+ [ Svg.Attributes.r "80"
+ , Svg.Attributes.stroke "silver"
+ , Svg.Attributes.strokeWidth "1"
+ , Svg.Attributes.strokeDasharray "5 5"
+ , Svg.Attributes.fill "none"
+ ]
+ []
+ in
+ shapes
+ |> Svg.svg
+ [ Svg.Attributes.viewBox "-100 -100 200 200"
+ ]
+ |> Element.html
+
+
+defaults : Config
+defaults =
+ { circle = False
+ , center = False
+ , angle = False
+ , dots = False
+ }
+
+
+type alias Config =
+ { circle : Bool
+ , center : Bool
+ , angle : Bool
+ , dots : Bool
+ }Make develop script build all examples to public/
It's a quick and dirty solution.
All examples are now in src/Examples/ directory.
There is a new Transformations module that exposes Transformation type and it's constructors and apply function.
index 568384b..3b0e90e 100755
--- a/scripts/develop
+++ b/scripts/develop
@@ -2,4 +2,10 @@
=
=set -euo pipefail
=
+# Build examples
+for example in src/Examples/*.elm
+do
+ npx elm make --output "public/${example}.html" "${example}"
+done
+
=npx elm-live src/Main.elm --pushstate -- --debugsimilarity index 98%
rename from src/CartesianCoordinates.elm
rename to src/Examples/CartesianCoordinates.elm
index fc29be3..1c4266c 100644
--- a/src/CartesianCoordinates.elm
+++ b/src/Examples/CartesianCoordinates.elm
@@ -1,4 +1,4 @@
-module CartesianCoordinates exposing
+module Examples.CartesianCoordinates exposing
= ( Model
= , Msg
= , initsimilarity index 96%
rename from src/CenteredDot.elm
rename to src/Examples/CenteredDot.elm
index 59fb692..9f00631 100644
--- a/src/CenteredDot.elm
+++ b/src/Examples/CenteredDot.elm
@@ -1,4 +1,4 @@
-module CenteredDot exposing (main)
+module Examples.CenteredDot exposing (main)
=
={-| This program demonstrates how to center an element within an SVG viewport. The idea is to make sure that (0, 0) point (so called origin) should be in the middle of the ViewBox. We can achive that, by making the top-left corner of the ViewBox same distance from the (0, 0) as the bottom right.
=similarity index 97%
rename from src/Counter.elm
rename to src/Examples/Counter.elm
index 0f0689c..66d1400 100644
--- a/src/Counter.elm
+++ b/src/Examples/Counter.elm
@@ -1,4 +1,4 @@
-module Counter exposing
+module Examples.Counter exposing
= ( Model
= , Msg
= , initsimilarity index 87%
rename from src/DotAtTheCenterOfTheScreen.elm
rename to src/Examples/DotAtTheCenterOfTheScreen.elm
index 83f0d1d..cc1875e 100644
--- a/src/DotAtTheCenterOfTheScreen.elm
+++ b/src/Examples/DotAtTheCenterOfTheScreen.elm
@@ -1,4 +1,4 @@
-module DotAtTheCenterOfTheScreen exposing (main, ui)
+module Examples.DotAtTheCenterOfTheScreen exposing (main, ui)
=
=import Element
=import Svgsimilarity index 90%
rename from src/FillTheScreen.elm
rename to src/Examples/FillTheScreen.elm
index 56f88d6..cef2371 100644
--- a/src/FillTheScreen.elm
+++ b/src/Examples/FillTheScreen.elm
@@ -1,4 +1,4 @@
-module FillTheScreen exposing (main, ui)
+module Examples.FillTheScreen exposing (main, ui)
=
=import Element
=import Svgsimilarity index 96%
rename from src/Gradient.elm
rename to src/Examples/Gradient.elm
index 4aa189c..1d7a0c9 100644
--- a/src/Gradient.elm
+++ b/src/Examples/Gradient.elm
@@ -1,4 +1,4 @@
-module Gradient exposing (main, ui)
+module Examples.Gradient exposing (main, ui)
=
=import Element
=import Svgsimilarity index 93%
rename from src/Line.elm
rename to src/Examples/Line.elm
index 53ab314..d105ef5 100644
--- a/src/Line.elm
+++ b/src/Examples/Line.elm
@@ -1,4 +1,4 @@
-module Line exposing (main, ui)
+module Examples.Line exposing (main, ui)
=
=import Element
=import Svgsimilarity index 96%
rename from src/LineTypedTransformations.elm
rename to src/Examples/LineTypedTransformations.elm
index f39d91f..46899d3 100644
--- a/src/LineTypedTransformations.elm
+++ b/src/Examples/LineTypedTransformations.elm
@@ -1,4 +1,4 @@
-module LineTypedTransformations exposing (main)
+module Examples.LineTypedTransformations exposing (main)
=
=import Element
=import Svgsimilarity index 98%
rename from src/NestedTransformations.elm
rename to src/Examples/NestedTransformations.elm
index e0aaded..b1bc6f5 100644
--- a/src/NestedTransformations.elm
+++ b/src/Examples/NestedTransformations.elm
@@ -1,4 +1,4 @@
-module NestedTransformations exposing
+module Examples.NestedTransformations exposing
= ( Model
= , Msg
= , init
@@ -26,8 +26,10 @@ import List.Extra as List
=import Point2d
=import Svg exposing (..)
=import Svg.Attributes exposing (..)
+import Transformations exposing (Transformation(..))
=
=
+main : Program () Model Msg
=main =
= Browser.sandbox
= { init = init
@@ -40,13 +42,6 @@ type alias Model =
= AnyDict String Group (Array Transformation)
=
=
-type Transformation
- = Identity
- | Scale Float Float
- | Translate Float Float
- | Rotate Float
-
-
=type Group
= = Pink
= | Green
@@ -134,7 +129,9 @@ ui model =
= nestTransformationsGroup group transformations item =
= let
= transformation =
- transformations |> Array.toList |> apply
+ transformations
+ |> Array.toList
+ |> Transformations.apply
=
= color =
= groupsimilarity index 99%
rename from src/PolarCoordinates.elm
rename to src/Examples/PolarCoordinates.elm
index 3a476cd..4fd1e93 100644
--- a/src/PolarCoordinates.elm
+++ b/src/Examples/PolarCoordinates.elm
@@ -1,4 +1,4 @@
-module PolarCoordinates exposing
+module Examples.PolarCoordinates exposing
= ( Model
= , Msg
= , initsimilarity index 98%
rename from src/RosetteTypedTransformations.elm
rename to src/Examples/RosetteTypedTransformations.elm
index 2ae161e..e83eb99 100644
--- a/src/RosetteTypedTransformations.elm
+++ b/src/Examples/RosetteTypedTransformations.elm
@@ -1,4 +1,4 @@
-module RosetteTypedTransformations exposing (main, ui)
+module Examples.RosetteTypedTransformations exposing (main, ui)
=
=import Element
=import Svgsimilarity index 89%
rename from src/Simplest.elm
rename to src/Examples/Simplest.elm
index 2e65fa4..74f75dc 100644
--- a/src/Simplest.elm
+++ b/src/Examples/Simplest.elm
@@ -1,4 +1,4 @@
-module Simplest exposing (Config, defaults, ui)
+module Examples.Simplest exposing (Config, defaults, ui)
=
=import Html exposing (Html)
=import Svgsimilarity index 98%
rename from src/Spiral.elm
rename to src/Examples/Spiral.elm
index 7e1ae87..3b52178 100644
--- a/src/Spiral.elm
+++ b/src/Examples/Spiral.elm
@@ -1,4 +1,4 @@
-module Spiral exposing (main, ui)
+module Examples.Spiral exposing (main, ui)
=
=import Element
=import Svgnew file mode 100644
index 0000000..3321826
--- /dev/null
+++ b/src/Examples/Transformations.elm
@@ -0,0 +1,343 @@
+module Examples.Transformations exposing
+ ( Model
+ , Msg
+ , init
+ , main
+ , ui
+ , update
+ )
+
+import Array exposing (Array)
+import Browser
+import Browser.Events
+import CartesianPlane
+import Dict exposing (Dict)
+import Element exposing (Element)
+import Element.Background as Background
+import Element.Border as Border
+import Element.Input as Input
+import Geometry.Svg
+import Html exposing (Html)
+import Json.Decode exposing (Decoder)
+import LineSegment2d
+import List.Extra as List
+import Point2d
+import Svg exposing (..)
+import Svg.Attributes exposing (..)
+import Transformations exposing (Transformation(..))
+
+
+main : Program () Model Msg
+main =
+ Browser.sandbox
+ { init = init
+ , view = view
+ , update = update
+ }
+
+
+type alias Model =
+ Array Transformation
+
+
+type Msg
+ = AddTransformation Transformation
+ | DeleteTransformation Int
+ | SetTransformation Int Transformation
+
+
+init : Model
+init =
+ Array.fromList
+ [ Translate 0 0
+ , Rotate 0
+ , Scale 1 1
+ ]
+
+
+view : Model -> Html Msg
+view model =
+ Element.layout
+ [ Element.height Element.fill
+ , Element.width Element.fill
+ ]
+ (ui model)
+
+
+ui : Model -> Element Msg
+ui model =
+ let
+ transformations =
+ Array.toList model
+
+ wrapper element =
+ Element.column
+ [ Element.width (Element.maximum 600 Element.fill)
+ , Element.centerX
+ , Element.spacing 20
+ ]
+ [ Element.el
+ [ Element.width Element.fill
+ ]
+ (Element.html element)
+ , Element.el
+ [ Background.color (Element.rgb 1 0.8 0.8)
+ , Element.padding 10
+ , Element.width Element.fill
+ ]
+ (transformationsUI transformations)
+ ]
+
+ shape =
+ g [ transform (Transformations.apply transformations) ]
+ [ line
+ [ x1 "0"
+ , x2 "100"
+ , y1 "0"
+ , y2 "0"
+ , stroke "red"
+ , strokeWidth "1"
+ ]
+ []
+ , circle [ cx "0", cy "0", r "2", fill "red" ] []
+ , grid
+ [ stroke "pink"
+ , fill "pink"
+ , strokeWidth "0.3"
+ ]
+ 10
+ 30
+
+ -- , rect [ x "", y "-2", width "1", height "4" ] []
+ ]
+ in
+ shape
+ |> List.singleton
+ |> CartesianPlane.graph 300
+ |> wrapper
+
+
+transformationsUI : List Transformation -> Element Msg
+transformationsUI transformations =
+ let
+ addButtons =
+ [ Element.text "Add transformation: "
+ , Input.button []
+ { onPress = Just (AddTransformation (Translate 0 0))
+ , label = Element.text "Translate"
+ }
+ , Input.button []
+ { onPress = Just (AddTransformation (Scale 1 1))
+ , label = Element.text "Scale"
+ }
+ , Input.button []
+ { onPress = Just (AddTransformation (Rotate 0))
+ , label = Element.text "Rotate"
+ }
+ ]
+
+ currentTrasformations =
+ transformations
+ |> List.indexedMap transformationUI
+ in
+ Element.column
+ [ Element.width Element.fill
+ , Element.spacing 10
+ ]
+ [ Element.row
+ [ Element.width Element.fill
+ , Element.spacing 10
+ ]
+ addButtons
+ , Element.column
+ [ Element.width Element.fill
+ , Element.spacing 10
+ ]
+ currentTrasformations
+ ]
+
+
+transformationUI : Int -> Transformation -> Element Msg
+transformationUI index transformation =
+ let
+ sliderBackground =
+ Element.el
+ [ Element.width Element.fill
+ , Element.height (Element.px 2)
+ , Element.centerY
+ , Background.color <| Element.rgb 0.7 0.7 0.7
+ , Border.rounded 2
+ ]
+ Element.none
+
+ controls =
+ case transformation of
+ Identity ->
+ [ Element.text "Identity" ]
+
+ Scale horizontal vertical ->
+ [ Input.slider
+ [ Element.behindContent sliderBackground
+ ]
+ { onChange =
+ \x ->
+ SetTransformation index (Scale x vertical)
+ , label = Input.labelLeft [] (Element.text "horizontal")
+ , min = 0
+ , max = 10
+ , value = horizontal
+ , thumb = Input.defaultThumb
+ , step = Just 0.1
+ }
+ , Input.slider
+ [ Element.behindContent sliderBackground
+ ]
+ { onChange =
+ \y ->
+ SetTransformation index (Scale horizontal y)
+ , label = Input.labelLeft [] (Element.text "vertical")
+ , min = 0
+ , max = 10
+ , value = vertical
+ , thumb = Input.defaultThumb
+ , step = Just 0.1
+ }
+ ]
+
+ Translate x y ->
+ [ Input.slider
+ [ Element.behindContent sliderBackground
+ ]
+ { onChange =
+ \value ->
+ SetTransformation index (Translate value y)
+ , label = Input.labelLeft [] (Element.text "x")
+ , min = -100
+ , max = 100
+ , value = x
+ , thumb = Input.defaultThumb
+ , step = Just 1
+ }
+ , Input.slider
+ [ Element.behindContent sliderBackground
+ ]
+ { onChange =
+ \value ->
+ SetTransformation index (Translate x value)
+ , label = Input.labelLeft [] (Element.text "y")
+ , min = -100
+ , max = 100
+ , value = y
+ , thumb = Input.defaultThumb
+ , step = Just 1
+ }
+ ]
+
+ Rotate angle ->
+ [ Input.slider
+ [ Element.behindContent sliderBackground
+ ]
+ { onChange =
+ \value ->
+ SetTransformation index (Rotate value)
+ , label = Input.labelLeft [] (Element.text "angle")
+ , min = -360
+ , max = 360
+ , value = angle
+ , thumb = Input.defaultThumb
+ , step = Just 1
+ }
+ ]
+ in
+ Element.column
+ [ Element.width Element.fill
+ , Border.color (Element.rgb 0.9 0.9 0.9)
+ , Border.width 3
+ , Element.padding 5
+ , Element.spacing 20
+ ]
+ [ Element.row [ Element.width Element.fill ]
+ [ transformation
+ |> List.singleton
+ |> Transformations.apply
+ |> Element.text
+ |> Element.el [ Element.width Element.fill ]
+ , Input.button []
+ { onPress = Just (DeleteTransformation index)
+ , label = Element.text "X"
+ }
+ ]
+ , Element.column
+ [ Element.width Element.fill
+ , Element.spacing 20
+ ]
+ controls
+ ]
+
+
+update : Msg -> Model -> Model
+update msg model =
+ case msg of
+ AddTransformation transformation ->
+ Array.push transformation model
+
+ DeleteTransformation index ->
+ let
+ end =
+ Array.length model
+
+ front =
+ Array.slice 0 index model
+
+ back =
+ Array.slice (index + 1) end model
+ in
+ Array.append front back
+
+ SetTransformation index transformation ->
+ Array.set index transformation model
+
+
+subscriptions : Model -> Sub Msg
+subscriptions model =
+ Sub.none
+
+
+grid : List (Svg.Attribute msg) -> Float -> Float -> Svg msg
+grid attributes unit size =
+ let
+ positiveValues =
+ size
+ / 2
+ |> floor
+ |> List.range 1
+ |> List.map toFloat
+ |> List.map ((*) unit)
+
+ negativeValues =
+ positiveValues
+ |> List.map negate
+
+ max =
+ unit * size / 2
+
+ min =
+ negate max
+ in
+ ((positiveValues ++ negativeValues)
+ |> List.map
+ (\value ->
+ [ ( Point2d.fromCoordinates ( value, min )
+ , Point2d.fromCoordinates ( value, max )
+ )
+ , ( Point2d.fromCoordinates ( min, value )
+ , Point2d.fromCoordinates ( max, value )
+ )
+ ]
+ )
+ |> List.concat
+ |> List.map LineSegment2d.fromEndpoints
+ |> List.map (Geometry.Svg.lineSegment2d attributes)
+ )
+ |> (::) (CartesianPlane.axes attributes (size * unit))
+ |> g []similarity index 98%
rename from src/TransformationsCircle.elm
rename to src/Examples/TransformationsCircle.elm
index 96264ff..a3a507f 100644
--- a/src/TransformationsCircle.elm
+++ b/src/Examples/TransformationsCircle.elm
@@ -1,4 +1,4 @@
-module TransformationsCircle exposing (Config, defaults, main, ui)
+module Examples.TransformationsCircle exposing (Config, defaults, main, ui)
=
=import Element exposing (Element)
=import Html exposing (Html)similarity index 99%
rename from src/Tree.elm
rename to src/Examples/Tree.elm
index e2925a8..eaed7b3 100644
--- a/src/Tree.elm
+++ b/src/Examples/Tree.elm
@@ -1,4 +1,4 @@
-module Tree exposing
+module Examples.Tree exposing
= ( Model
= , Msg
= , init
@@ -19,6 +19,7 @@ import Svg exposing (..)
=import Svg.Attributes exposing (..)
=
=
+main : Program Flags Model Msg
=main =
= Browser.element
= { init = initnew file mode 100644
index 0000000..222da7a
--- /dev/null
+++ b/src/Examples/ViewBox.elm
@@ -0,0 +1,276 @@
+module Examples.ViewBox exposing
+ ( Model
+ , Msg
+ , init
+ , main
+ , ui
+ , update
+ , view
+ )
+
+import Browser
+import BrowserWindow
+import CartesianPlane exposing (graph)
+import Element
+import Element.Background as Background
+import Element.Border as Border
+import Element.Input as Input
+import Html exposing (Html)
+import Svg exposing (..)
+import Svg.Attributes exposing (..)
+
+
+main =
+ Browser.sandbox
+ { init = init
+ , view = view
+ , update = update
+ }
+
+
+type alias Model =
+ { left : Float
+ , top : Float
+ , width : Float
+ , height : Float
+ , preserveAspectRatio : Bool
+ }
+
+
+type Msg
+ = Move Float Float
+ | Resize Float Float
+ | PreserveAspectRatio Bool
+
+
+init : Model
+init =
+ { left = 0
+ , top = 0
+ , width = 400
+ , height = 400
+ , preserveAspectRatio = False
+ }
+
+
+view : Model -> Html.Html Msg
+view model =
+ let
+ wrapper element =
+ Element.el
+ [ Element.width (Element.maximum 1200 Element.fill)
+ , Element.height Element.fill
+ , Element.centerX
+ ]
+ element
+ in
+ ui model
+ |> wrapper
+ |> Element.layout
+ [ Element.height Element.fill
+ , Element.width Element.fill
+ ]
+
+
+update : Msg -> Model -> Model
+update msg model =
+ case msg of
+ Move left top ->
+ { model
+ | left = left
+ , top = top
+ }
+
+ Resize width height ->
+ { model
+ | width = width
+ , height = height
+ }
+
+ PreserveAspectRatio value ->
+ { model | preserveAspectRatio = value }
+
+
+ui model =
+ let
+ viewbox =
+ svg
+ [ viewBox "-500 -500 1000 1000"
+ ]
+ [ g [] world
+ , rect
+ [ x (String.fromFloat model.left)
+ , y (String.fromFloat model.top)
+ , width (String.fromFloat model.width)
+ , height (String.fromFloat model.height)
+ , opacity "0.3"
+ , stroke "white"
+ , fill "pink"
+ ]
+ []
+ ]
+ |> Element.html
+ |> Element.el
+ [ Element.centerX
+ , Element.centerY
+ , Element.width Element.fill
+ , Element.height Element.fill
+ ]
+
+ viewport =
+ svg
+ [ [ model.left, model.top, model.width, model.height ]
+ |> List.map String.fromFloat
+ |> String.join " "
+ |> viewBox
+ , preserveAspectRatio <|
+ case model.preserveAspectRatio of
+ True ->
+ "xMidYMid meet"
+
+ False ->
+ "none"
+ , Svg.Attributes.style "width: 100%; height: 100%; flex-grow: 1; flex-basis: 0"
+ ]
+ [ g [] world
+ , rect
+ [ x (String.fromFloat model.left)
+ , y (String.fromFloat model.top)
+ , width (String.fromFloat model.width)
+ , height (String.fromFloat model.height)
+ , opacity "0.3"
+ , stroke "white"
+ , fill "pink"
+ ]
+ []
+ ]
+ |> Element.html
+ |> Element.el
+ [ Element.width Element.fill
+ , Element.height Element.fill
+ ]
+ |> BrowserWindow.window
+ [ Element.centerX
+ , Element.centerY
+ , Element.width Element.fill
+ , Element.height Element.fill
+ ]
+ in
+ Element.column
+ [ Element.width Element.fill
+ , Element.height Element.fill
+ , Element.spacing 30
+ , Element.padding 30
+ ]
+ [ Element.row
+ [ Element.width Element.fill
+ , Element.height Element.fill
+ , Element.centerX
+ , Element.spacing 30
+ ]
+ [ viewbox
+ , viewport
+ ]
+ , Input.slider
+ [ Element.behindContent
+ (Element.el
+ [ Element.width Element.fill
+ , Element.height (Element.px 2)
+ , Element.centerY
+ , Background.color <| Element.rgb 0.7 0.7 0.7
+ , Border.rounded 2
+ ]
+ Element.none
+ )
+ ]
+ { onChange = \left -> Move left model.top
+ , label =
+ Input.labelBelow [ Element.centerX ] <|
+ Element.text ("left: " ++ String.fromFloat model.left)
+ , min = -500
+ , max = 500
+ , value = model.left
+ , thumb = Input.defaultThumb
+ , step = Just 1
+ }
+ , Input.slider
+ [ Element.behindContent
+ (Element.el
+ [ Element.width Element.fill
+ , Element.height (Element.px 2)
+ , Element.centerY
+ , Background.color <| Element.rgb 0.7 0.7 0.7
+ , Border.rounded 2
+ ]
+ Element.none
+ )
+ ]
+ { onChange = \top -> Move model.left top
+ , label =
+ Input.labelBelow [ Element.centerX ] <|
+ Element.text ("top: " ++ String.fromFloat model.top)
+ , min = -500
+ , max = 500
+ , value = model.top
+ , thumb = Input.defaultThumb
+ , step = Just 1
+ }
+ , Input.slider
+ [ Element.behindContent
+ (Element.el
+ [ Element.width Element.fill
+ , Element.height (Element.px 2)
+ , Element.centerY
+ , Background.color <| Element.rgb 0.7 0.7 0.7
+ , Border.rounded 2
+ ]
+ Element.none
+ )
+ ]
+ { onChange = \width -> Resize width model.height
+ , label =
+ Input.labelBelow [ Element.centerX ] <|
+ Element.text ("width: " ++ String.fromFloat model.width)
+ , min = 1
+ , max = 500
+ , value = model.width
+ , thumb = Input.defaultThumb
+ , step = Just 1
+ }
+ , Input.slider
+ [ Element.behindContent
+ (Element.el
+ [ Element.width Element.fill
+ , Element.height (Element.px 2)
+ , Element.centerY
+ , Background.color <| Element.rgb 0.7 0.7 0.7
+ , Border.rounded 2
+ ]
+ Element.none
+ )
+ ]
+ { onChange = \height -> Resize model.width height
+ , label =
+ Input.labelBelow [ Element.centerX ] <|
+ Element.text ("height: " ++ String.fromFloat model.height)
+ , min = 1
+ , max = 500
+ , value = model.height
+ , thumb = Input.defaultThumb
+ , step = Just 1
+ }
+ , Input.checkbox []
+ { checked = model.preserveAspectRatio
+ , icon = Input.defaultCheckbox
+ , label = Input.labelRight [] (Element.text "Preserve aspect ratio")
+ , onChange = PreserveAspectRatio
+ }
+ ]
+
+
+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" ] []
+ ]index ab8ac60..99d1276 100644
--- a/src/Main.elm
+++ b/src/Main.elm
@@ -10,41 +10,41 @@ import Basics.Extra exposing (curry)
=import Browser
=import Browser.Navigation as Navigation
=import BrowserWindow
-import CartesianCoordinates
-import CenteredDot
-import Counter
=import Dict
-import DotAtTheCenterOfTheScreen
=import Element exposing (Element)
=import Element.Background as Background
=import Element.Border as Border
=import Element.Events
=import Element.Font as Font
=import Element.Input as Input
+import Examples.CartesianCoordinates
+import Examples.CenteredDot
+import Examples.Counter
+import Examples.DotAtTheCenterOfTheScreen
+import Examples.FillTheScreen
+import Examples.Gradient
+import Examples.Line
+import Examples.NestedTransformations
+import Examples.PolarCoordinates
+import Examples.RosetteTypedTransformations
+import Examples.Simplest
+import Examples.Spiral
+import Examples.Transformations
+import Examples.TransformationsCircle
+import Examples.Tree
+import Examples.ViewBox
=import FeatherIcons exposing (icons)
-import FillTheScreen
-import Gradient
=import Html exposing (Html)
=import Html.Attributes
=import Http
-import Line
=import Mark
=import Mark.Custom
=import Mark.Default
-import NestedTransformations
=import Parser
=import Parser.Advanced
-import PolarCoordinates
=import Result.Extra as Result
-import RosetteTypedTransformations
=import Routes exposing (Route)
-import Simplest
-import Spiral
-import Transformations
-import TransformationsCircle
-import Tree
=import Url exposing (Url)
-import ViewBox
=
=
=main : Program Flags Model Msg
@@ -67,13 +67,13 @@ type alias Model =
= { url : Url
= , key : Navigation.Key
= , markup : Maybe String
- , counter : Counter.Model
- , transformations : Transformations.Model
- , nestedTransformations : NestedTransformations.Model
- , cartesianCoordinates : CartesianCoordinates.Model
- , polarCoordinates : PolarCoordinates.Model
- , tree : Maybe Tree.Model
- , viewBox : ViewBox.Model
+ , counter : Examples.Counter.Model
+ , transformations : Examples.Transformations.Model
+ , nestedTransformations : Examples.NestedTransformations.Model
+ , cartesianCoordinates : Examples.CartesianCoordinates.Model
+ , polarCoordinates : Examples.PolarCoordinates.Model
+ , tree : Maybe Examples.Tree.Model
+ , viewBox : Examples.ViewBox.Model
= }
=
=
@@ -81,14 +81,14 @@ type Msg
= = UrlRequested Browser.UrlRequest
= | UrlChanged Url
= | ContentFetched (Result Http.Error String)
- | CounterMsg Counter.Msg
- | TransformationsMsg Transformations.Msg
- | NestedTransformationsMsg NestedTransformations.Msg
- | CartesianCoordinatesMsg CartesianCoordinates.Msg
- | PolarCoordinatesMsg PolarCoordinates.Msg
- | ToggleTree (Maybe Tree.Model)
- | TreeMsg Tree.Msg
- | ViewBoxMsg ViewBox.Msg
+ | CounterMsg Examples.Counter.Msg
+ | TransformationsMsg Examples.Transformations.Msg
+ | NestedTransformationsMsg Examples.NestedTransformations.Msg
+ | CartesianCoordinatesMsg Examples.CartesianCoordinates.Msg
+ | PolarCoordinatesMsg Examples.PolarCoordinates.Msg
+ | ToggleTree (Maybe Examples.Tree.Model)
+ | TreeMsg Examples.Tree.Msg
+ | ViewBoxMsg Examples.ViewBox.Msg
=
=
=init : Flags -> Url -> Navigation.Key -> ( Model, Cmd Msg )
@@ -96,13 +96,13 @@ init flags url key =
= ( { url = url
= , key = key
= , markup = Nothing
- , counter = Counter.init
- , transformations = Transformations.init
- , nestedTransformations = NestedTransformations.init
- , cartesianCoordinates = CartesianCoordinates.init
- , polarCoordinates = PolarCoordinates.init
+ , counter = Examples.Counter.init
+ , transformations = Examples.Transformations.init
+ , nestedTransformations = Examples.NestedTransformations.init
+ , cartesianCoordinates = Examples.CartesianCoordinates.init
+ , polarCoordinates = Examples.PolarCoordinates.init
= , tree = Nothing
- , viewBox = ViewBox.init
+ , viewBox = Examples.ViewBox.init
= }
= , url
= |> Routes.parse
@@ -307,27 +307,39 @@ update msg model =
= )
=
= CounterMsg m ->
- ( { model | counter = Counter.update m model.counter }
+ ( { model | counter = Examples.Counter.update m model.counter }
= , Cmd.none
= )
=
= TransformationsMsg m ->
- ( { model | transformations = Transformations.update m model.transformations }
+ ( { model
+ | transformations =
+ Examples.Transformations.update m model.transformations
+ }
= , Cmd.none
= )
=
= NestedTransformationsMsg m ->
- ( { model | nestedTransformations = NestedTransformations.update m model.nestedTransformations }
+ ( { model
+ | nestedTransformations =
+ Examples.NestedTransformations.update m model.nestedTransformations
+ }
= , Cmd.none
= )
=
= CartesianCoordinatesMsg m ->
- ( { model | cartesianCoordinates = CartesianCoordinates.update m model.cartesianCoordinates }
+ ( { model
+ | cartesianCoordinates =
+ Examples.CartesianCoordinates.update m model.cartesianCoordinates
+ }
= , Cmd.none
= )
=
= PolarCoordinatesMsg m ->
- ( { model | polarCoordinates = PolarCoordinates.update m model.polarCoordinates }
+ ( { model
+ | polarCoordinates =
+ Examples.PolarCoordinates.update m model.polarCoordinates
+ }
= , Cmd.none
= )
=
@@ -337,7 +349,7 @@ update msg model =
= TreeMsg m ->
= case model.tree of
= Just tree ->
- Tree.update m tree
+ Examples.Tree.update m tree
= |> (\( newTree, treeCmd ) ->
= ( { model | tree = Just newTree }
= , Cmd.map TreeMsg treeCmd
@@ -348,7 +360,7 @@ update msg model =
= ( model, Cmd.none )
=
= ViewBoxMsg m ->
- ( { model | viewBox = ViewBox.update m model.viewBox }, Cmd.none )
+ ( { model | viewBox = Examples.ViewBox.update m model.viewBox }, Cmd.none )
=
=
=subscriptions : Model -> Sub Msg
@@ -358,7 +370,7 @@ subscriptions model =
= Sub.none
=
= Just tree ->
- Tree.subscriptions tree
+ Examples.Tree.subscriptions tree
= |> Sub.map TreeMsg
=
=
@@ -472,7 +484,7 @@ document =
= let
= render model =
= model.counter
- |> Counter.ui
+ |> Examples.Counter.ui
= |> Element.el
= [ Element.centerX
= , Element.centerY
@@ -489,9 +501,9 @@ document =
= simplest : Mark.Block (Model -> Element Msg)
= simplest =
= let
- render : Simplest.Config -> Model -> Element Msg
+ render : Examples.Simplest.Config -> Model -> Element Msg
= render config model =
- Simplest.ui config
+ Examples.Simplest.ui config
= |> Element.html
= |> Element.el []
= |> Element.el
@@ -502,7 +514,7 @@ document =
= |> BrowserWindow.window []
= in
= Mark.record2 "Simplest"
- Simplest.Config
+ Examples.Simplest.Config
= (Mark.field "background" Mark.string)
= (Mark.field "fill" Mark.string)
= |> Mark.map render
@@ -511,7 +523,7 @@ document =
= fillTheScreen =
= let
= render model =
- FillTheScreen.ui
+ Examples.FillTheScreen.ui
= |> Element.el
= [ Element.height (Element.px 400)
= , Element.width Element.fill
@@ -524,7 +536,7 @@ document =
= dotAtTheCenterOfTheScreen =
= let
= render model =
- DotAtTheCenterOfTheScreen.ui
+ Examples.DotAtTheCenterOfTheScreen.ui
= |> Element.el
= [ Element.height (Element.px 400)
= , Element.width Element.fill
@@ -537,7 +549,7 @@ document =
= centeredDot =
= let
= render model =
- CenteredDot.main
+ Examples.CenteredDot.main
= |> Element.html
= |> Element.el
= [ Element.height (Element.px 400)
@@ -550,9 +562,9 @@ document =
= transformationsCircle : Mark.Block (Model -> Element Msg)
= transformationsCircle =
= let
- render : TransformationsCircle.Config -> Model -> Element Msg
+ render : Examples.TransformationsCircle.Config -> Model -> Element Msg
= render config model =
- TransformationsCircle.ui config
+ Examples.TransformationsCircle.ui config
= |> Element.el
= [ Element.height (Element.px 400)
= , Element.width Element.fill
@@ -561,7 +573,7 @@ document =
= |> BrowserWindow.window []
= in
= Mark.record4 "TransformationsCircle"
- TransformationsCircle.Config
+ Examples.TransformationsCircle.Config
= (Mark.field "circle" Mark.bool)
= (Mark.field "center" Mark.bool)
= (Mark.field "angle" Mark.bool)
@@ -572,7 +584,7 @@ document =
= line =
= let
= render model =
- Line.ui
+ Examples.Line.ui
= |> Element.el
= [ Element.height (Element.px 400)
= , Element.width Element.fill
@@ -585,7 +597,7 @@ document =
= gradient =
= let
= render model =
- Gradient.ui
+ Examples.Gradient.ui
= |> Element.el
= [ Element.height (Element.px 400)
= , Element.width Element.fill
@@ -599,7 +611,7 @@ document =
= let
= render model =
= model.transformations
- |> Transformations.ui
+ |> Examples.Transformations.ui
= |> Element.el
= [ Element.width Element.fill
= ]
@@ -613,7 +625,7 @@ document =
= let
= render model =
= model.nestedTransformations
- |> NestedTransformations.ui
+ |> Examples.NestedTransformations.ui
= |> Element.el
= [ Element.width Element.fill
= ]
@@ -627,7 +639,7 @@ document =
= let
= render model =
= model.cartesianCoordinates
- |> CartesianCoordinates.ui
+ |> Examples.CartesianCoordinates.ui
= |> Element.el
= [ Element.width Element.fill
= ]
@@ -641,7 +653,7 @@ document =
= let
= render model =
= model.polarCoordinates
- |> PolarCoordinates.ui
+ |> Examples.PolarCoordinates.ui
= |> Element.el
= [ Element.width Element.fill
= ]
@@ -654,7 +666,7 @@ document =
= rosette =
= let
= render model =
- RosetteTypedTransformations.ui
+ Examples.RosetteTypedTransformations.ui
= |> Element.el
= [ Element.height (Element.px 400)
= , Element.width Element.fill
@@ -667,7 +679,7 @@ document =
= spiral =
= let
= render model =
- Spiral.ui
+ Examples.Spiral.ui
= |> Element.el
= [ Element.height (Element.px 400)
= , Element.width Element.fill
@@ -681,7 +693,7 @@ document =
= let
= render model =
= model.tree
- |> Maybe.map Tree.ui
+ |> Maybe.map Examples.Tree.ui
= |> Maybe.withDefault Element.none
= |> Element.el
= [ Element.height (Element.px 600)
@@ -697,7 +709,7 @@ document =
= let
= render model =
= model.viewBox
- |> ViewBox.ui
+ |> Examples.ViewBox.ui
= |> Element.el
= [ Element.width Element.fill
= ]index 1b3937d..d72965f 100644
--- a/src/Transformations.elm
+++ b/src/Transformations.elm
@@ -1,45 +1,8 @@
=module Transformations exposing
- ( Model
- , Msg
- , Transformation(..)
+ ( Transformation(..)
= , apply
- , init
- , main
- , ui
- , update
= )
=
-import Array exposing (Array)
-import Browser
-import Browser.Events
-import CartesianPlane
-import Dict exposing (Dict)
-import Element exposing (Element)
-import Element.Background as Background
-import Element.Border as Border
-import Element.Input as Input
-import Geometry.Svg
-import Html exposing (Html)
-import Json.Decode exposing (Decoder)
-import LineSegment2d
-import List.Extra as List
-import Point2d
-import Svg exposing (..)
-import Svg.Attributes exposing (..)
-
-
-main : Program () Model Msg
-main =
- Browser.sandbox
- { init = init
- , view = view
- , update = update
- }
-
-
-type alias Model =
- Array Transformation
-
=
=type Transformation
= = Identity
@@ -48,269 +11,6 @@ type Transformation
= | Rotate Float
=
=
-type Msg
- = AddTransformation Transformation
- | DeleteTransformation Int
- | SetTransformation Int Transformation
-
-
-init : Model
-init =
- Array.fromList
- [ Translate 0 0
- , Rotate 0
- , Scale 1 1
- ]
-
-
-view : Model -> Html Msg
-view model =
- Element.layout
- [ Element.height Element.fill
- , Element.width Element.fill
- ]
- (ui model)
-
-
-ui : Model -> Element Msg
-ui model =
- let
- transformations =
- Array.toList model
-
- wrapper element =
- Element.column
- [ Element.width (Element.maximum 600 Element.fill)
- , Element.centerX
- , Element.spacing 20
- ]
- [ Element.el
- [ Element.width Element.fill
- ]
- (Element.html element)
- , Element.el
- [ Background.color (Element.rgb 1 0.8 0.8)
- , Element.padding 10
- , Element.width Element.fill
- ]
- (transformationsUI transformations)
- ]
-
- shape =
- g [ transform (apply transformations) ]
- [ line
- [ x1 "0"
- , x2 "100"
- , y1 "0"
- , y2 "0"
- , stroke "red"
- , strokeWidth "1"
- ]
- []
- , circle [ cx "0", cy "0", r "2", fill "red" ] []
- , grid
- [ stroke "pink"
- , fill "pink"
- , strokeWidth "0.3"
- ]
- 10
- 30
-
- -- , rect [ x "", y "-2", width "1", height "4" ] []
- ]
- in
- shape
- |> List.singleton
- |> CartesianPlane.graph 300
- |> wrapper
-
-
-transformationsUI : List Transformation -> Element Msg
-transformationsUI transformations =
- let
- addButtons =
- [ Element.text "Add transformation: "
- , Input.button []
- { onPress = Just (AddTransformation (Translate 0 0))
- , label = Element.text "Translate"
- }
- , Input.button []
- { onPress = Just (AddTransformation (Scale 1 1))
- , label = Element.text "Scale"
- }
- , Input.button []
- { onPress = Just (AddTransformation (Rotate 0))
- , label = Element.text "Rotate"
- }
- ]
-
- currentTrasformations =
- transformations
- |> List.indexedMap transformationUI
- in
- Element.column
- [ Element.width Element.fill
- , Element.spacing 10
- ]
- [ Element.row
- [ Element.width Element.fill
- , Element.spacing 10
- ]
- addButtons
- , Element.column
- [ Element.width Element.fill
- , Element.spacing 10
- ]
- currentTrasformations
- ]
-
-
-transformationUI : Int -> Transformation -> Element Msg
-transformationUI index transformation =
- let
- sliderBackground =
- Element.el
- [ Element.width Element.fill
- , Element.height (Element.px 2)
- , Element.centerY
- , Background.color <| Element.rgb 0.7 0.7 0.7
- , Border.rounded 2
- ]
- Element.none
-
- controls =
- case transformation of
- Identity ->
- [ Element.text "Identity" ]
-
- Scale horizontal vertical ->
- [ Input.slider
- [ Element.behindContent sliderBackground
- ]
- { onChange =
- \x ->
- SetTransformation index (Scale x vertical)
- , label = Input.labelLeft [] (Element.text "horizontal")
- , min = 0
- , max = 10
- , value = horizontal
- , thumb = Input.defaultThumb
- , step = Just 0.1
- }
- , Input.slider
- [ Element.behindContent sliderBackground
- ]
- { onChange =
- \y ->
- SetTransformation index (Scale horizontal y)
- , label = Input.labelLeft [] (Element.text "vertical")
- , min = 0
- , max = 10
- , value = vertical
- , thumb = Input.defaultThumb
- , step = Just 0.1
- }
- ]
-
- Translate x y ->
- [ Input.slider
- [ Element.behindContent sliderBackground
- ]
- { onChange =
- \value ->
- SetTransformation index (Translate value y)
- , label = Input.labelLeft [] (Element.text "x")
- , min = -100
- , max = 100
- , value = x
- , thumb = Input.defaultThumb
- , step = Just 1
- }
- , Input.slider
- [ Element.behindContent sliderBackground
- ]
- { onChange =
- \value ->
- SetTransformation index (Translate x value)
- , label = Input.labelLeft [] (Element.text "y")
- , min = -100
- , max = 100
- , value = y
- , thumb = Input.defaultThumb
- , step = Just 1
- }
- ]
-
- Rotate angle ->
- [ Input.slider
- [ Element.behindContent sliderBackground
- ]
- { onChange =
- \value ->
- SetTransformation index (Rotate value)
- , label = Input.labelLeft [] (Element.text "angle")
- , min = -360
- , max = 360
- , value = angle
- , thumb = Input.defaultThumb
- , step = Just 1
- }
- ]
- in
- Element.column
- [ Element.width Element.fill
- , Border.color (Element.rgb 0.9 0.9 0.9)
- , Border.width 3
- , Element.padding 5
- , Element.spacing 20
- ]
- [ Element.row [ Element.width Element.fill ]
- [ transformation
- |> List.singleton
- |> apply
- |> Element.text
- |> Element.el [ Element.width Element.fill ]
- , Input.button []
- { onPress = Just (DeleteTransformation index)
- , label = Element.text "X"
- }
- ]
- , Element.column
- [ Element.width Element.fill
- , Element.spacing 20
- ]
- controls
- ]
-
-
-update : Msg -> Model -> Model
-update msg model =
- case msg of
- AddTransformation transformation ->
- Array.push transformation model
-
- DeleteTransformation index ->
- let
- end =
- Array.length model
-
- front =
- Array.slice 0 index model
-
- back =
- Array.slice (index + 1) end model
- in
- Array.append front back
-
- SetTransformation index transformation ->
- Array.set index transformation model
-
-
-subscriptions : Model -> Sub Msg
-subscriptions model =
- Sub.none
-
-
=apply : List Transformation -> String
=apply transformations =
= let
@@ -342,43 +42,3 @@ apply transformations =
= transformations
= |> List.map toString
= |> String.join " "
-
-
-grid : List (Svg.Attribute msg) -> Float -> Float -> Svg msg
-grid attributes unit size =
- let
- positiveValues =
- size
- / 2
- |> floor
- |> List.range 1
- |> List.map toFloat
- |> List.map ((*) unit)
-
- negativeValues =
- positiveValues
- |> List.map negate
-
- max =
- unit * size / 2
-
- min =
- negate max
- in
- ((positiveValues ++ negativeValues)
- |> List.map
- (\value ->
- [ ( Point2d.fromCoordinates ( value, min )
- , Point2d.fromCoordinates ( value, max )
- )
- , ( Point2d.fromCoordinates ( min, value )
- , Point2d.fromCoordinates ( max, value )
- )
- ]
- )
- |> List.concat
- |> List.map LineSegment2d.fromEndpoints
- |> List.map (Geometry.Svg.lineSegment2d attributes)
- )
- |> (::) (CartesianPlane.axes attributes (size * unit))
- |> g []