letterpress/source/chapter5.adoc
2024-07-07 22:43:39 +02:00

277 lines
8.5 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

== Chapter 5: Programming Fractals
=== Introduction
Fractals are complex patterns that are self-similar across different scales. They are fascinating both in their mathematical properties and their visual appeal. In this chapter, we will explore how to program fractals using Elixir, BQN, and Haskell, focusing on some of the most famous fractal patterns such as the Mandelbrot set and the Sierpinski triangle.
=== The Mandelbrot Set
The Mandelbrot set is a set of complex numbers that produces a distinctive and famous fractal shape. The set is defined by iterating the function:
[stem]
++++
z_{n+1} = z_n^2 + c
++++
==== Plotting the Mandelbrot Set in Elixir
Let's start with a basic Elixir program to plot the Mandelbrot set.
[source,elixir]
----
defmodule Mandelbrot do
def mandelbrot(c, max_iter) do
iterate(c, 0, 0, max_iter)
end
defp iterate(_c, _z, n, max_iter) when n >= max_iter, do: n
defp iterate(c, z, n, max_iter) when abs(z) > 2, do: n
defp iterate(c, z, n, max_iter) do
iterate(c, z*z + c, n + 1, max_iter)
end
def mandelbrot_set(xmin, xmax, ymin, ymax, width, height, max_iter) do
for x <- 0..(width-1), y <- 0..(height-1) do
r = xmin + (xmax - xmin) * x / width
i = ymin + (ymax - ymin) * y / height
c = Complex.new(r, i)
{x, y, mandelbrot(c, max_iter)}
end
end
end
{:ok, _} = :application.ensure_all_started(:gnuplot)
plot = Mandelbrot.mandelbrot_set(-2.0, 1.0, -1.5, 1.5, 800, 800, 256)
:Gnuplot.plot(plot)
----
==== Plotting the Mandelbrot Set in BQN
Here's a BQN program to plot the Mandelbrot set.
[source,bqn]
----
mandelbrot ← {
z ← 0
for n ≤ ⍟ do
if /2≤´|z then n else
z +← z×z + ⍟
end
end
n
}
mandelbrotSet ← {
r ← (⍟1+⌊○)∧!⌊○1,1⊸
⌽⊏ { ⊏⍟(⍟1×⍟1)⊣mandelbrot¨r¨r}¨⊸∾¨⊸∾
}
xmin xmax ymin ymax width height max_iter ← ⊏-2 1 ¯1.5 1.5 800 800 256
plot ← mandelbrotSet xmin xmax ymin ymax width height max_iter
plot
----
==== Plotting the Mandelbrot Set in Haskell
Here's a Haskell program to plot the Mandelbrot set.
[source,haskell]
----
import Data.Complex
import Codec.Picture
mandelbrot :: Complex Double -> Int -> Int
mandelbrot c maxIter = length . takeWhile ((<= 2) . magnitude) . take maxIter $ iterate (\z -> z*z + c) 0
mandelbrotSet :: Double -> Double -> Double -> Double -> Int -> Int -> Int -> Image Pixel8
mandelbrotSet xmin xmax ymin ymax width height maxIter = generateImage pixelRenderer width height
where
pixelRenderer x y = let
real = xmin + (xmax - xmin) * fromIntegral x / fromIntegral width
imag = ymin + (ymax - ymin) * fromIntegral y / fromIntegral height
c = real :+ imag
iter = mandelbrot c maxIter
in fromIntegral (255 * iter `div` maxIter)
main :: IO ()
main = savePngImage "mandelbrot.png" (ImageY8 $ mandelbrotSet (-2.0) 1.0 (-1.5) 1.5 800 800 256)
----
=== The Sierpinski Triangle
The Sierpinski triangle is a fractal that is formed by recursively subdividing an equilateral triangle into smaller equilateral triangles.
==== Plotting the Sierpinski Triangle in Elixir
Here is an Elixir program to plot the Sierpinski triangle using recursion.
[source,elixir]
----
defmodule Sierpinski do
def draw_triangle(x, y, size, depth, canvas) when depth == 0 do
triangle = [{x, y}, {x + size / 2, y + size * :math.sqrt(3) / 2}, {x + size, y}]
:egd.filled_polygon(canvas, triangle, :egd.color(:black))
end
def draw_triangle(x, y, size, depth, canvas) do
draw_triangle(x, y, size / 2, depth - 1, canvas)
draw_triangle(x + size / 2, y, size / 2, depth - 1, canvas)
draw_triangle(x + size / 4, y + size * :math.sqrt(3) / 4, size / 2, depth - 1, canvas)
end
end
{:ok, canvas} = :egd.create(800, 800)
Sierpinski.draw_triangle(0, 0, 800, 5, canvas)
:image.create(:png, canvas)
----
==== Plotting the Sierpinski Triangle in BQN
Here is a BQN program to plot the Sierpinski triangle using recursion.
[source,bqn]
----
sierpinski ← {
size depth canvas ← ⍟
if depth = 0 then canvas
else
sierpinski size/2 depth-1 canvas (size/2) size
sierpinski size/2 depth-1 canvas ⊹⌽(size/2) size
sierpinski size/2 depth-1 canvas ⊹⌽(size/4) (size*√3/4)
end
}
sierpinski 800 5 []
----
==== Plotting the Sierpinski Triangle in Haskell
Here is a Haskell program to plot the Sierpinski triangle using recursion.
[source,haskell]
----
import Codec.Picture
drawTriangle :: Int -> Int -> Int -> Int -> Image Pixel8 -> Image Pixel8
drawTriangle x y size depth img
| depth == 0 = drawFilledPolygon img [(x, y), (x + size `div` 2, y + round (fromIntegral size * sqrt 3 / 2)), (x + size, y)] 0
| otherwise = drawTriangle x y (size `div` 2) (depth - 1) .
drawTriangle (x + size `div` 2) y (size `div` 2) (depth - 1) .
drawTriangle (x + size `div` 4) (y + round (fromIntegral size * sqrt 3 / 4)) (size `div` 2) (depth - 1) $ img
main :: IO ()
main = savePngImage "sierpinski.png" (ImageY8 $ drawTriangle 0 0 800 5 (generateImage (\_ _ -> 255) 800 800))
----
=== Julia Sets
Julia sets are another type of fractal, closely related to the Mandelbrot set. They are generated using a similar iterative function but with a fixed complex parameter.
==== Plotting a Julia Set in Elixir
Here's an Elixir program to plot a Julia set.
[source,elixir]
----
defmodule Julia do
def julia(c, z, max_iter) do
iterate(c, z, 0, max_iter)
end
defp iterate(_c, _z, n, max_iter) when n >= max_iter, do: n
defp iterate(c, z, n, max_iter) when abs(z) > 2, do: n
defp iterate(c, z, n, max_iter) do
iterate(c, z*z + c, n + 1, max_iter)
end
def julia_set(c, xmin, xmax, ymin, ymax, width, height, max_iter) do
for x <- 0..(width-1), y <- 0..(height-1) do
r = xmin + (xmax - xmin) * x / width
i = ymin + (ymax - ymin) * y / height
z = Complex.new(r, i)
{x, y, julia(c, z, max_iter)}
end
end
end
c = Complex.new(-0.7, 0.27015)
plot = Julia.julia_set(c, -1.5, 1.5, -1.5, 1.5, 800, 800, 256)
:Gnuplot.plot(plot)
----
==== Plotting a Julia Set in BQN
Here's a BQN program to plot a Julia set.
[source,bqn]
----
julia ← { z c max_iter ← ⍟
iterate ← { z +← z×z + c}
for n ≤ max_iter do
if /2≤´|z then n else iterate z
end
}
juliaSet ← { c xmin xmax ymin ymax width height max_iter ← ⍟
r1 ← (⍟1+⌊○)∧!⌊○1,1⊸
⌽⊏ { ⊏⍟(⍟1×⍟1)⊣julia¨r1¨r1¨c¨max_iter}¨⊸∾¨⊸∾
}
c = 0.27015J¯0.7
xmin xmax ymin ymax width height max_iter ← ⊏-1.5 1.5 ¯1.5 1.5 800 800 256
plot ← juliaSet c xmin xmax ymin ymax width height max_iter
plot
----
==== Plotting a Julia Set in Haskell
Here's a Haskell program to plot a Julia set.
[source,haskell]
----
import Data.Complex
import Codec.Picture
julia :: Complex Double -> Complex Double -> Int -> Int
julia c z0 maxIter = length . takeWhile ((<= 2) . magnitude) . take maxIter $ iterate (\z -> z*z + c) z0
juliaSet :: Complex Double -> Double -> Double -> Double -> Double -> Int -> Int -> Int -> Image Pixel8
juliaSet c xmin xmax ymin ymax width height maxIter = generateImage pixelRenderer width height
where
pixelRenderer x y = let
real = xmin + (xmax - xmin) * fromIntegral x / fromIntegral width
imag = ymin + (ymax - ymin) * fromIntegral y / fromIntegral height
z0 = real :+ imag
iter = julia c z0 maxIter
in fromIntegral (255 * iter `div` maxIter)
main :: IO ()
main = savePngImage "julia.png" (ImageY8 $ juliaSet (0.27015 :+ (-0.7)) (-1.5) 1.5 (-1.5) 1.5 800 800 256)
----
=== Conclusion
Fractals are an excellent way to understand the beauty and complexity of mathematical patterns. By programming these patterns, we not only appreciate their aesthetic appeal but also gain insights into their mathematical properties.
=== Further Reading
For more information on fractals and programming, check out the following resources:
* https://en.wikipedia.org/wiki/Mandelbrot_set[Mandelbrot Set - Wikipedia]
* https://en.wikipedia.org/wiki/Sierpinski_triangle[Sierpinski Triangle - Wikipedia]
* https://en.wikipedia.org/wiki/Julia_set[Julia Set - Wikipedia]
=== References
1. Mandelbrot, B. B. (1982). _The Fractal Geometry of Nature_. New York: W.H. Freeman and Company.
2. Peitgen, H.-O., Jürgens, H., & Saupe, D. (1992). _Chaos and Fractals: New Frontiers of Science_. New York: Springer.
=== Appendix
==== Image and Code Credits
* Mandelbrot Set: Code adapted from various fractal tutorials in Elixir, BQN, and Haskell.
* Sierpinski Triangle: Code adapted from various fractal tutorials in Elixir, BQN, and Haskell.
* Julia Set: Code adapted from various fractal tutorials in Elixir, BQN, and Haskell.