
A Barnsley fern is a fractal named after British mathematician Michael Barnsley and can be created using an iterated function system (IFS).



Task

Create this fractal fern, using the following transformations:

 ƒ1   (chosen 1% of the time)
        xn + 1 = 0
        yn + 1 = 0.16 yn

 ƒ2   (chosen 85% of the time)
        xn + 1 = 0.85 xn + 0.04 yn
        yn + 1 = −0.04 xn + 0.85 yn + 1.6

 ƒ3   (chosen 7% of the time)
        xn + 1 = 0.2 xn − 0.26 yn
        yn + 1 = 0.23 xn + 0.22 yn + 1.6

 ƒ4   (chosen 7% of the time)
        xn + 1 = −0.15 xn + 0.28 yn
        yn + 1 = 0.26 xn + 0.24 yn + 0.44.

Starting position: x = 0, y = 0




import Data.List (scanl')
import Diagrams.Backend.Rasterific.CmdLine
import Diagrams.Prelude
import System.Random
 
type Pt  = (Double, Double)
 
-- Four affine transformations used to produce a Barnsley fern.
f1, f2, f3, f4 :: Pt -> Pt
f1 (x, y) = (                       0,             0.16 * y)
f2 (x, y) = ( 0.85 * x + 0.04 * y    , -0.04 * x + 0.85 * y + 1.60)
f3 (x, y) = ( 0.20 * x - 0.26 * y    ,  0.23 * x + 0.22 * y + 1.60)
f4 (x, y) = (-0.15 * x + 0.28 * y    ,  0.26 * x + 0.24 * y + 0.44)
 
-- Given a random number in [0, 1) transform an initial point by a randomly
-- chosen function.
func :: Pt -> Double -> Pt
func p r | r < 0.01  = f1 p
         | r < 0.86  = f2 p
         | r < 0.93  = f3 p
         | otherwise = f4 p
 
-- Using a sequence of uniformly distributed random numbers in [0, 1) return
-- the same number of points in the fern.
fern :: [Double] -> [Pt]
fern = scanl' func (0, 0)
 
-- Given a supply of random values and a count, generate a diagram of a fern
-- composed of that number of points.
drawFern :: [Double] -> Int -> Diagram B
drawFern rs n = frame 0.5 . diagramFrom . take n $ fern rs
  where diagramFrom = flip atPoints (repeat dot) . map p2
        dot = circle 0.005 # lc green
 
-- To generate a PNG image of a fern, call this program like:
--
--   fern -o fern.png -w 640 -h 640 50000
--
-- where the arguments specify the width, height and number of points in the
-- image.
main :: IO ()
main = do
  rand <- getStdGen
  mainWith $ drawFern (randomRs (0, 1) rand)