Generate and draw a fractal tree.

 Draw the trunk
 At the end of the trunk, split by some angle and draw two branches
 Repeat at the end of each branch until a sufficient level of branching is reached


Related tasks
 Pythagoras Tree


module FractalTree where
 
import Java.IO
import Prelude.Math
 
data AffineTransform = native java.awt.geom.AffineTransform where
  native new :: () -> STMutable s AffineTransform
  native clone :: Mutable s AffineTransform -> STMutable s AffineTransform
  native rotate :: Mutable s AffineTransform -> Double -> ST s ()
  native scale :: Mutable s AffineTransform -> Double -> Double -> ST s ()
  native translate :: Mutable s AffineTransform -> Double -> Double -> ST s ()
 
data BufferedImage = native java.awt.image.BufferedImage where
  pure native type_3byte_bgr "java.awt.image.BufferedImage.TYPE_3BYTE_BGR" :: Int
  native new :: Int -> Int -> Int -> STMutable s BufferedImage
  native createGraphics :: Mutable s BufferedImage -> STMutable s Graphics2D
 
data Color = pure native java.awt.Color where
  pure native black "java.awt.Color.black" :: Color
  pure native green "java.awt.Color.green" :: Color
  pure native white "java.awt.Color.white" :: Color
  pure native new :: Int -> Color
 
data BasicStroke = pure native java.awt.BasicStroke where
  pure native new :: Float -> BasicStroke
 
data RenderingHints = native java.awt.RenderingHints where
  pure native key_antialiasing "java.awt.RenderingHints.KEY_ANTIALIASING" :: RenderingHints_Key
  pure native value_antialias_on "java.awt.RenderingHints.VALUE_ANTIALIAS_ON" :: Object
 
data RenderingHints_Key = pure native java.awt.RenderingHints.Key
 
data Graphics2D = native java.awt.Graphics2D where
  native drawLine :: Mutable s Graphics2D -> Int -> Int -> Int -> Int -> ST s ()
  native drawOval :: Mutable s Graphics2D -> Int -> Int -> Int -> Int -> ST s ()
  native fillRect :: Mutable s Graphics2D -> Int -> Int -> Int -> Int -> ST s ()
  native setColor :: Mutable s Graphics2D -> Color -> ST s ()
  native setRenderingHint :: Mutable s Graphics2D -> RenderingHints_Key -> Object -> ST s ()
  native setStroke :: Mutable s Graphics2D -> BasicStroke -> ST s ()
  native setTransform :: Mutable s Graphics2D -> Mutable s AffineTransform -> ST s ()
 
data ImageIO = mutable native javax.imageio.ImageIO where
  native write "javax.imageio.ImageIO.write" :: MutableIO BufferedImage -> String -> MutableIO File -> IO Bool throws IOException
 
drawTree :: Mutable s Graphics2D -> Mutable s AffineTransform -> Int -> ST s ()
drawTree g t i = do
  let len = 10 -- ratio of length to thickness
      shrink = 0.75
      angle = 0.3 -- radians
      i' = i - 1
  g.setTransform t
  g.drawLine 0 0 0 len
  when (i' > 0) $ do
    t.translate 0 (fromIntegral len)
    t.scale shrink shrink
    rt <- t.clone
    t.rotate angle
    rt.rotate (-angle)
    drawTree g t i'
    drawTree g rt i'
 
main = do
  let width = 900
      height = 800
      initScale = 20
      halfWidth = fromIntegral width / 2
  buffy <- BufferedImage.new width height BufferedImage.type_3byte_bgr
  g <- buffy.createGraphics
  g.setRenderingHint RenderingHints.key_antialiasing RenderingHints.value_antialias_on
  g.setColor Color.black
  g.fillRect 0 0 width height
  g.setColor Color.green
  t <- AffineTransform.new ()
  t.translate halfWidth (fromIntegral height)
  t.scale initScale initScale
  t.rotate pi
  drawTree g t 16
  f <- File.new "FractalTreeFrege.png"
  void $ ImageIO.write buffy "png" f