
The Archimedean spiral is a spiral named after the Greek mathematician Archimedes.



An Archimedean spiral can be described by the equation:






r
=
a
+
b
θ


{\displaystyle \,r=a+b\theta }


with real numbers a and b.



Task
Draw an Archimedean spiral.




module Archimedean where
 
import Java.IO
import Prelude.Math
 
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 orange "java.awt.Color.orange" :: 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 ()
 
data ImageIO = mutable native javax.imageio.ImageIO where
  native write "javax.imageio.ImageIO.write" :: MutableIO BufferedImage -> String -> MutableIO File -> IO Bool throws IOException
 
width = 640
center = width `div` 2
 
roundi = fromIntegral . round
 
drawGrid :: Mutable s Graphics2D -> ST s ()
drawGrid g = do
  g.setColor $ Color.new 0xEEEEEE
  g.setStroke $ BasicStroke.new 2
  let angle = toRadians 45
      margin = 10
      numRings = 8
      spacing = (width - 2 * margin) `div` (numRings * 2)
  forM_ [0 .. numRings-1] $ \i -> do
    let pos = margin + i * spacing
        size = width - (2 * margin + i * 2 * spacing)
        ia = fromIntegral i * angle
        multiplier = fromIntegral $ (width - 2 * margin) `div` 2
        x2 = center + (roundi (cos ia * multiplier))
        y2 = center - (roundi (sin ia * multiplier))
    g.drawOval pos pos size size
    g.drawLine center center x2 y2
 
drawSpiral :: Mutable s Graphics2D -> ST s ()
drawSpiral g = do
  g.setStroke $ BasicStroke.new 2
  g.setColor $ Color.orange
  let degrees = toRadians 0.1
      end = 360 * 2 * 10 * degrees
      a = 0
      b = 20
      c = 1
      drSp theta = do
        let r = a + b * theta ** (1 / c)
            x = r * cos theta
            y = r * sin theta
            theta' = theta + degrees
        plot g (center + roundi x) (center - roundi y)
        when (theta' < end) (drSp (theta' + degrees))
  drSp 0
 
plot :: Mutable s Graphics2D -> Int -> Int -> ST s ()
plot g x y = g.drawOval x y 1 1
 
main = do
  buffy <- BufferedImage.new width width BufferedImage.type_3byte_bgr
  g <- buffy.createGraphics
  g.setRenderingHint RenderingHints.key_antialiasing RenderingHints.value_antialias_on
  g.setColor Color.white
  g.fillRect 0 0 width width
  drawGrid g
  drawSpiral g
  f <- File.new "SpiralFrege.png"
  void $ ImageIO.write buffy "png" f