{-|
Module : Graphics.Autom.NextNearest
Description : 1-D binary cellular automata with next-nearest-neighbor updating
Copyright : (c) Christopher Howard, 2016
License : GPL-3
Maintainer : ch.howard@zoho.com
This module implements the core algorithm for 1-D binary cellular
automata with next-nearest-neighbor updating.
-}
module Graphics.Autom.NextNearest (grid, overlaidGrid) where
import Prelude ((-), (+), mod, Bool, Int, (*), div, undefined, take, map, foldr)
import Data.Vector.Unboxed ((!), Unbox, Vector, length, fromList, generate, concat)
import Data.Word (Word32)
import Data.Bits (testBit, (.|.), shift)
-- |Starting with a single row of binary values, this function
-- generates a vector of rows in which each row (except the first) is
-- based on the values of the previous row, using the next-nearest
-- neighbors calculation based upon the 32 bit rule provided.
grid :: Vector Bool -- ^ the initial row of binary values
-> Word32 -- ^ the 32-bit rule
-> Int -- ^ the number of rows returned
-> Vector Bool -- ^ the rows returned concatenated into a single vector
grid v r h = concat (take h (go v))
where go a = let n = newRow a r in n : go n
-- |This function overlays each point in a grid with the 32-bit value
-- category it falls into. This is a simple method of categorizing
-- pixels in a generated pattern, so that color and masks can be
-- applied to them in interesting ways.
overlaidGrid :: Vector Bool -- ^ a grid (set of rows) concatenated in a single vector
-> Int -- ^ the width of a row in the grid
-> Vector (Bool, Int)
overlaidGrid v w = fromList (map f [ (r, c)
| r <- [0 .. (rows - 1)]
, c <- [0 .. (w - 1)] ])
where rows = length v `div` w
f (r, c) = ( indexSeamlessGrid v w (r, c)
, fiveBitInt ( indexSeamlessGrid v w (r - 1, c - 2)
, indexSeamlessGrid v w (r - 1, c - 1)
, indexSeamlessGrid v w (r - 1, c )
, indexSeamlessGrid v w (r - 1, c + 1)
, indexSeamlessGrid v w (r - 1, c + 2) ))
newRow :: Vector Bool -> Word32 -> Vector Bool
newRow v r = generate (length v)
(\i -> testBit r (nextNearestInt v i))
fiveBitInt :: (Bool, Bool, Bool, Bool, Bool) -> Int
fiveBitInt (a, b, c, d, e) =
let toi x = if x then 1 else 0 in
foldr (.|.) 0 [ shift (toi a) 4 :: Int
, shift (toi b) 3 :: Int
, shift (toi c) 2 :: Int
, shift (toi d) 1 :: Int
, toi e :: Int ]
indexSeamlessGrid :: Unbox a => Vector a -> Int -> (Int, Int) -> a
indexSeamlessGrid v w (r, c) = v ! i
where i = wr * w + wc
wr = mod r (length v `div` w)
wc = mod c w
wrappedIndex :: Unbox a => Vector a -> Int -> a
wrappedIndex v i = v ! mod i (length v)
nextNearestInt :: Vector Bool -> Int -> Int
nextNearestInt v i = fiveBitInt ( wrappedIndex v (i - 2)
, wrappedIndex v (i - 1)
, wrappedIndex v i
, wrappedIndex v (i + 1)
, wrappedIndex v (i + 2))