packages feed

gloss-examples-1.7.2.1: raster/Crystal/Main.hs

-- Quasicrystals demo. 
--  
-- Based on code from:
--   http://mainisusuallyafunction.blogspot.com/2011/10/quasicrystals-as-sums-of-waves-in-plane.html
--
{-# LANGUAGE BangPatterns #-}
import Graphics.Gloss.Raster.Field
import System.Environment

-- Types ----------------------------------------------------------------------
-- | Angle in radians.
type Angle  = Float

-- | Angle offset used for animation.
type Phi    = Float

-- | Number of waves to sum for each pixel.
type Degree = Int

-- | Feature size of visualisation.
type Scale  = Float

-- | Time in seconds since the program started.
type Time   = Float


-- Point ----------------------------------------------------------------------
-- | Compute a single point of the visualisation.
quasicrystal :: Scale -> Degree -> Time -> Point -> Color
quasicrystal !scale !degree !time !p
 = let  -- Scale the time to be the phi value of the animation.
        -- The action seems to slow down at increasing phi values, 
        -- so we increase phi faster as time moves on.
        phi     = 1 + (time ** 1.5) * 0.005

   in   rampColor 
          $ waves degree phi
          $ point scale p


-- | Sum up all the waves at a particular point.
waves :: Degree -> Phi -> Point -> Float
waves !degree !phi !x = wrap $ waver 0 degree
 where
    !th = pi / phi

    waver :: Float -> Int -> Float
    waver !acc !n
     | n == 0    = acc
     | otherwise = waver (acc + wave (fromIntegral n * th) x)
                         (n - 1)
         
    wrap n 
     = let !n_  = truncate n :: Int
           !n'  = n - fromIntegral n_
       in  if odd n_ then 1 - n'
                     else n'


-- | Generate the value for a single wave.
wave :: Angle -> Point -> Float
wave !th = f where
    !cth  = cos th
    !sth  = sin th

    {-# INLINE f #-}
    f (x, y)  = (cos (cth*x + sth*y) + 1) / 2


-- | Convert an image point to a point on our wave plane.
point :: Scale -> Point -> Point
point !scale (x, y) = (x * scale, y * scale)


-- | Color ramp from blue to white.
rampColor :: Float -> Color
rampColor v
 = rawColor v (0.4 + (v * 0.6)) 1 1


-- Main -----------------------------------------------------------------------
main :: IO ()
main 
 = do   args    <- getArgs
        case args of
         []     -> run 800 600 2 30 5

         [sizeX, sizeY, zoom, scale, degree]
                -> run (read sizeX) (read sizeY) (read zoom) (read scale) (read degree)

         _ -> putStr $ unlines
           [ "quazicrystal <sizeX::Int> <sizeY::Int> <zoom::Int> <scale::Float> <degree::Int>"
           , "    sizeX, sizeY - visualisation size                  (default 800, 600)"
           , "    zoom         - pixel replication factor            (default 5)"
           , "    scale        - feature size of visualisation       (default 30)"
           , "    degree       - number waves to sum for each point  (default 5)" 
           , ""
           , " You'll want to run this with +RTS -N to enable threads" ]
   

run :: Int -> Int -> Int -> Scale -> Degree -> IO ()                     
run sizeX sizeY zoom scale degree
 = animateField (InWindow "Crystal" (sizeX, sizeY) (10, 10)) 
        (zoom, zoom)
        (quasicrystal scale degree)