diff --git a/README.md b/README.md
index 10feb2f..d6fa1b2 100644
--- a/README.md
+++ b/README.md
@@ -41,10 +41,41 @@ You should see something like this
## How does it work
A vector connects the origin to a specific point `(x, y)`. You can create one
-like this: `Vector.build(0, 1)`.
+like this:
-A box is defined by three vectors, `a`, `b` and `c`. In the picture below, `a`
-is red, `b` is orange and `c` is purple.
+```elixir
+iex(1)> Vector.build(1, 0)
+%Vector{x: 1, y: 0}
+```
+
+Once we have vectors, we can describe geometrical operations on vectors such as
+adding, subtracting, negating and scaling them:
+
+```elixir
+iex(1)> Vector.add(Vector.build(1, 0), Vector.build(1, 1))
+%Vector{x: 2, y: 1}
+
+iex(2)> Vector.sub(Vector.build(1, 0), Vector.build(1, 1))
+%Vector{x: 0, y: -1}
+
+iex(3)> Vector.neg(Vector.build(1, 2))
+%Vector{x: -1, y: -2}
+
+iex(4)> Vector.scale(4, Vector.build(1, 0))
+%Vector{x: 4, y: 0}
+```
+
+A box is defined by three vectors, `a`, `b` and `c`.
+
+```elixir
+box = %Box{
+ a: Vector.build(75.0, 75.0),
+ b: Vector.build(500.0, 0.0),
+ c: Vector.build(0.0, 500.0)
+}
+```
+
+In the picture below, `a` is red, `b` is orange and `c` is purple.
@@ -132,7 +163,7 @@ end
This is exactly what the `Fitting.create_picture` function does!
-Now let's say we wanted to draw the letter `F`. We could write a function like this:
+Now let's say we wanted to draw the letter `f`. We could write a function like this:
```elixir
def f do
@@ -160,34 +191,416 @@ This would look like this!
The amazing thing about defining a picture as an anonymous function is that we
can transform the picture by calling functions on the box. For example, if we
-had a `turn_box` function that rotated the box left, we could then rotate the
+had a `turn` function that rotated the box left, we could then rotate the
picture by just calling that.
```elixir
-def turn_box(%Box{a: a, b: b, c: c}) do
- %Box{
- a: Vector.add(a, b),
- b: c,
- c: Vector.neg(b)
- }
+defmodule Box do
+ def turn(%Box{a: a, b: b, c: c}) do
+ %Box{
+ a: Vector.add(a, b),
+ b: c,
+ c: Vector.neg(b)
+ }
+ end
end
-def turn_picture(picture) do
- fn box ->
- box
- |> turn_box()
- |> picture.()
+defmodule Picture do
+ def turn(picture) do
+ fn box ->
+ box
+ |> Box.turn()
+ |> picture.()
+ end
end
end
```
-If we applied this code to our `F` we would see something like this:
+If we applied this code to our `f` we would see something like this:
+In a similar way, we could describe the horizontal flipping of a picture as
+an horizontal flipping of the box that contains such picture.
-So this is how you would hook the whole thing together:
+```elixir
+defmodule Box do
+ def flip(%Box{a: a, b: b, c: c}) do
+ %Box{
+ a: Vector.add(a, b),
+ b: Vector.neg(b),
+ c: c
+ }
+ end
+end
+
+defmodule Picture do
+ def flip(picture) do
+ fn box ->
+ box
+ |> Box.flip()
+ |> picture.()
+ end
+ end
+end
+```
+
+And if we applied this to our `f` letter we would obtain this:
+
+
+
+What if we wanted to display two pictures side by side? We can think of a
+function that takes a box and returns a tuple `{left_box, right_box}`:
+
+```elixir
+defmodule Box do
+ def split_vertically(%Box{a: a, b: b, c: c}) do
+ left_box = %Box{
+ a: a,
+ b: Vector.scale(0.5, b),
+ c: c
+ }
+
+ right_box = %Box{
+ a: Vector.add(a, Vector.scale(0.5, b)),
+ b: Vector.scale(0.5, b),
+ c: c
+ }
+
+ {left_box, right_box}
+ end
+end
+```
+
+Then we can build a function that takes two pictures and applies the first one
+to the left box and the second one to the right box:
+
+```elixir
+defmodule Picture do
+ def beside(p1, p2) do
+ fn box ->
+ {left_box, right_box} = Box.split_vertically(box)
+
+ p1.(left_box) ++ p2.(right_box)
+ end
+ end
+end
+```
+
+Applying this function to two `f` letters would look like this:
+
+
+
+But what if we applied this function to `f` and `Picture.flip(f)`? Then this
+would happen!
+
+
+
+Pretty neat, no?
+
+It turns out we can generalize the above functions to specify a ratio, so we can
+control how much space we want to allocate to the left and right.
+
+```elixir
+defmodule Box do
+ def split_horizontally(factor, %Box{a: a, b: b, c: c}) do
+ above_ratio = factor
+
+ below_ratio = 1 - above_ratio
+
+ above = %Box{
+ a: Vector.add(a, Vector.scale(below_ratio, c)),
+ b: b,
+ c: Vector.scale(above_ratio, c)
+ }
+
+ below = %Box{
+ a: a,
+ b: b,
+ c: Vector.scale(below_ratio, c)
+ }
+
+ {above, below}
+ end
+end
+
+defmodule Picture do
+ def beside_ratio(m, n, p1, p2) do
+ fn box ->
+ factor = m / (m + n)
+
+ {box_left, box_right} = Box.split_vertically(factor, box)
+
+ p1.(box_left) ++ p2.(box_right)
+ end
+ end
+
+ def beside(p1, p2) do
+ beside_ratio(1, 1, p1, p2)
+ end
+end
+```
+
+With these functions we could write something like `Picture.beside_ratio(1, 2, f, f)` and obtain something like this:
+
+
+
+Now imagine that just like our `beside` and `beside_ratio` functions, we would
+have another couple of functions that are called `above` and `above_ratio`,
+which would position two pictures above one another. You can check out their
+implementation in `lib/picture.ex`.
+
+With those functions in place we can implement a function that takes four
+pictures and creates a quartet:
+
+```elixir
+def quartet(p1, p2, p3, p4) do
+ above(
+ beside(p1, p2),
+ beside(p3, p4)
+ )
+end
+```
+
+Applying this to four `f` letters would look like this:
+
+
+
+Similarly, we could write a function that puts nine pictures together in a
+three-by-three grid:
+
+```elixir
+def nonet(p1, p2, p3, p4, p5, p6, p7, p8, p9) do
+ above_ratio(
+ 1,
+ 2,
+ beside_ratio(1, 2, p1, beside(p2, p3)),
+ above(
+ beside_ratio(1, 2, p4, beside(p5, p6)),
+ beside_ratio(1, 2, p7, beside(p8, p9))
+ )
+ )
+end
+```
+
+And if we apply nine `f` letters to this function we would see this:
+
+
+As a fun intermezzo, let's implement a function that takes a picture and throws
+it into the air. We will rotate the image by 45 degrees and shrink its area by
+half:
+
+```elixir
+defmodule Box do
+ def toss(%Box{a: a, b: b, c: c}) do
+ %Box{
+ a: Vector.add(a, Vector.scale(0.5, Vector.add(b, c))),
+ b: Vector.scale(0.5, Vector.add(b, c)),
+ c: Vector.scale(0.5, Vector.add(c, Vector.neg(b)))
+ }
+ end
+end
+
+defmodule Picture do
+ def toss(picture) do
+ fn box ->
+ box
+ |> Box.toss()
+ |> picture.()
+ end
+ end
+end
+```
+
+You don't need to worry too much about the vector arithmetics, here's what it
+would look like:
+
+
+
+Cool!
+
+Now let's take a look at a fish.
+
+
+
+This image has some really interesting properties. Let's define a `over`
+function that stacks a bunch of pictures on top of one another:
+
+```elixir
+defmodule Picture do
+ def over(list) when is_list(list) do
+ fn box ->
+ Enum.flat_map(list, fn elem ->
+ elem.(box)
+ end)
+ end
+ end
+end
+```
+
+Applying this to `fish` and `fish |> turn |> turn` yields this
+
+
+
+Now we will define a function called `ttile` which is fundamental in building
+the fractal structure of the painting. We will see that the fish pattern is
+indeed amazing:
+
+```elixir
+def ttile(fish) do
+ fn box ->
+ side = fish |> toss |> flip
+
+ over([
+ fish,
+ side |> turn,
+ side |> turn |> turn
+ ]).(box)
+ end
+end
+```
+
+
+
+And our final basic block is the tile which will build the diagonals of our
+square, the `utile` function:
+
+```elixir
+def utile(fish) do
+ fn box ->
+ side = fish |> toss |> flip
+
+ over([
+ side,
+ side |> turn,
+ side |> turn |> turn,
+ side |> turn |> turn |> turn
+ ]).(box)
+ end
+end
+```
+
+
+
+With these tiles we can now create a recursive function called `side` which
+creates the side of our square. This function will take a parameter which
+specifies the depth of the recursion.
+
+```
+def side(0, _fish), do: fn _ -> [] end
+
+def side(n, fish) when n > 0 do
+ fn box ->
+ quartet(
+ side(n - 1, fish),
+ side(n - 1, fish),
+ turn(ttile(fish)),
+ ttile(fish)
+ ).(box)
+ end
+end
+```
+
+- If we pass `0`, we will just have an empty picture.
+- If we pass `1`, we wil have something that looks like this
+
+
+
+- If we pass `2`, we wil have something that looks like this
+
+
+
+This is starting to look great!
+
+Now that we have our sides, we can build corners! `corner` is another recursive
+function that will take a parameter which denotes the depth of the recursion:
+
+```elixir
+def corner(0, _fish), do: fn _ -> [] end
+
+def corner(n, fish) when n > 0 do
+ fn box ->
+ quartet(
+ corner(n - 1, fish),
+ side(n - 1, fish),
+ side(n - 1, fish) |> turn,
+ utile(fish)
+ ).(box)
+ end
+end
+```
+
+- If we pass `0`, we will just have an empty picture.
+- If we pass `1`, we wil have something that looks like this
+
+
+
+- If we pass `2`, we wil have something that looks like this
+
+
+
+Finally, the last step!
+
+By combining a central `utile` and a sequence of `side` and `corner` we can
+implement the Square Limit.
+
+```elixir
+def square_limit(0, _fish), do: fn _ -> [] end
+
+def square_limit(n, fish) when n > 0 do
+ fn box ->
+ corner = corner(n - 1, fish)
+ side = side(n - 1, fish)
+
+ nw = corner
+ nc = side
+ ne = corner |> turn |> turn |> turn
+ mw = side |> turn
+ mc = utile(fish)
+ me = side |> turn |> turn |> turn
+ sw = corner |> turn
+ sc = side |> turn |> turn
+ se = corner |> turn |> turn
+
+ nonet(
+ nw,
+ nc,
+ ne,
+ mw,
+ mc,
+ me,
+ sw,
+ sc,
+ se
+ ).(box)
+ end
+end
+```
+
+Let's see how it looks with different depths:
+
+- With depth `1`, it's just a `utile`
+
+
+
+- With depth `2`, it's a `utile` surrounded by a line of sides and corners
+
+
+
+- With depth `3`, we've added another layer around the previous one
+
+
+
+- With depth `5`, it's looking really great
+
+
+
+Mission complete!
+
+## Scenic integration
+
+So this is how you would hook the whole thing together:
```elixir
Graph.build(font: :roboto, font_size: 24, theme: :light)
diff --git a/images/fish.png b/images/fish.png
new file mode 100644
index 0000000..934cf41
Binary files /dev/null and b/images/fish.png differ
diff --git a/images/fish_corner_1.png b/images/fish_corner_1.png
new file mode 100644
index 0000000..cec967d
Binary files /dev/null and b/images/fish_corner_1.png differ
diff --git a/images/fish_corner_2.png b/images/fish_corner_2.png
new file mode 100644
index 0000000..f16f33f
Binary files /dev/null and b/images/fish_corner_2.png differ
diff --git a/images/fish_over.png b/images/fish_over.png
new file mode 100644
index 0000000..d69d85c
Binary files /dev/null and b/images/fish_over.png differ
diff --git a/images/fish_side_1.png b/images/fish_side_1.png
new file mode 100644
index 0000000..c7554ba
Binary files /dev/null and b/images/fish_side_1.png differ
diff --git a/images/fish_side_2.png b/images/fish_side_2.png
new file mode 100644
index 0000000..591b59b
Binary files /dev/null and b/images/fish_side_2.png differ
diff --git a/images/fish_ttile.png b/images/fish_ttile.png
new file mode 100644
index 0000000..4365aa8
Binary files /dev/null and b/images/fish_ttile.png differ
diff --git a/images/fish_utile.png b/images/fish_utile.png
new file mode 100644
index 0000000..72d8626
Binary files /dev/null and b/images/fish_utile.png differ
diff --git a/images/letter_f_beside.png b/images/letter_f_beside.png
new file mode 100644
index 0000000..8599353
Binary files /dev/null and b/images/letter_f_beside.png differ
diff --git a/images/letter_f_beside_flipped.png b/images/letter_f_beside_flipped.png
new file mode 100644
index 0000000..868050e
Binary files /dev/null and b/images/letter_f_beside_flipped.png differ
diff --git a/images/letter_f_beside_ratio.png b/images/letter_f_beside_ratio.png
new file mode 100644
index 0000000..d079378
Binary files /dev/null and b/images/letter_f_beside_ratio.png differ
diff --git a/images/letter_f_flipped.png b/images/letter_f_flipped.png
new file mode 100644
index 0000000..b6c5626
Binary files /dev/null and b/images/letter_f_flipped.png differ
diff --git a/images/letter_f_nonet.png b/images/letter_f_nonet.png
new file mode 100644
index 0000000..9bbc6d2
Binary files /dev/null and b/images/letter_f_nonet.png differ
diff --git a/images/letter_f_quartet.png b/images/letter_f_quartet.png
new file mode 100644
index 0000000..762b89b
Binary files /dev/null and b/images/letter_f_quartet.png differ
diff --git a/images/letter_f_tossed.png b/images/letter_f_tossed.png
new file mode 100644
index 0000000..fc60230
Binary files /dev/null and b/images/letter_f_tossed.png differ
diff --git a/images/square_limit_1.png b/images/square_limit_1.png
new file mode 100644
index 0000000..890fda7
Binary files /dev/null and b/images/square_limit_1.png differ
diff --git a/images/square_limit_2.png b/images/square_limit_2.png
new file mode 100644
index 0000000..0ce1b5e
Binary files /dev/null and b/images/square_limit_2.png differ
diff --git a/images/square_limit_3.png b/images/square_limit_3.png
new file mode 100644
index 0000000..7f49e5b
Binary files /dev/null and b/images/square_limit_3.png differ
diff --git a/lib/box.ex b/lib/box.ex
index 6595f88..6d6f379 100644
--- a/lib/box.ex
+++ b/lib/box.ex
@@ -18,7 +18,7 @@ defmodule Box do
end
def flip(%Box{a: a, b: b, c: c}) do
- %Box{a: Vector.add(a, c), b: b, c: Vector.neg(c)}
+ %Box{a: Vector.add(a, b), b: Vector.neg(b), c: c}
end
def toss(%Box{a: a, b: b, c: c}) do
diff --git a/lib/picture.ex b/lib/picture.ex
index 88fab72..a187e6e 100644
--- a/lib/picture.ex
+++ b/lib/picture.ex
@@ -93,12 +93,12 @@ defmodule Picture do
def ttile(fish) do
fn box ->
- side = fish |> toss |> flip
+ side = fish |> toss
over([
fish,
- side |> turn,
- side |> turn |> turn
+ side |> flip,
+ side |> turn |> flip
]).(box)
end
end
diff --git a/lib/scenes/home.ex b/lib/scenes/home.ex
index 9bb6019..f1ba53f 100644
--- a/lib/scenes/home.ex
+++ b/lib/scenes/home.ex
@@ -4,6 +4,7 @@ defmodule ScenicEscher.Scene.Home do
alias Scenic.Graph
import Scenic.Primitives
+ import Picture
@graph Graph.build(font: :roboto, font_size: 24, theme: :light)
|> group(
@@ -11,8 +12,8 @@ defmodule ScenicEscher.Scene.Home do
# We create a box
box = %Box{
a: Vector.build(75.0, 75.0),
- b: Vector.build(500.0, 0.0),
- c: Vector.build(0.0, 500.0)
+ b: Vector.build(400.0, 0.0),
+ c: Vector.build(0.0, 400.0)
}
# We create a fish
@@ -34,7 +35,7 @@ defmodule ScenicEscher.Scene.Home do
path(acc, elem, options)
end)
end,
- translate: {20, 60}
+ translate: {75, 75}
)
def init(_, _) do