packages feed

accelerate-0.7.1.0: examples/rasterize/rasterize.hs

{-
  Copyright (C) 2010 by IPwn Studios
  Released under GNU General Public License v3
-}
import qualified RasterizeAcc

import Control.Applicative
import Control.Arrow
import Data.Array (Array)
import Data.Array.IArray as A
import qualified Data.Array.Accelerate as Acc
import qualified Data.Array.Accelerate.Interpreter as Acc
import Data.List
import Data.Ord
import Debug.Trace
import System.IO

main :: IO ()
main = do
    test rasterizeH "rasterize-test1.txt"
    test rasterizeH "rasterize-test2.txt"
    test rasterizeH "rasterize-test3.txt"
    test rasterizeH "rasterize-test4.txt"
    test rasterizeAI "rasterize-test1.txt"
    test rasterizeAI "rasterize-test2.txt"
    test rasterizeAI "rasterize-test3.txt"
    test rasterizeAI "rasterize-test4.txt"
  where
    rasterizeH  = (rasterize, "Haskell")
    rasterizeAI = (rasterizeAI_, "Accelerate interpreted")
    rasterizeAI_ :: Area -> [Value] -> [Facet] -> [Float]
    rasterizeAI_ area@((x0,y0),(x1,y1)) values facets =
        let acc = RasterizeAcc.rasterize 
                    (Acc.tuple (Acc.tuple (Acc.constant x0, Acc.constant y0),
                                Acc.tuple (Acc.constant x1, Acc.constant y1)))
                    (Acc.use $ Acc.fromList (length values) values)
                    (Acc.use $ Acc.fromList (length facets) facets)
            out = Acc.toList $ Acc.run acc
        in  out    
      where
        v2acc ((x, y), v) = Acc.tuple (Acc.tuple (Acc.constant x, Acc.constant y), Acc.constant v)
        f2acc (a, b, c)   = Acc.tuple (Acc.constant a, Acc.constant b, Acc.constant c)

test :: (Area -> [Insertion] -> [Facet] -> [Float], String) -> FilePath -> IO ()
test (rast, descr) fn = do
    (values, facets, area, sb) <- withFile fn ReadMode $ \h -> do
        values <- read <$> hGetLine h 
        facets <- read <$> hGetLine h
        area   <- read <$> hGetLine h
        sb     <- read <$> hGetLine h
        return (values, facets, area, sb)
    let is = rast area values facets
    if sb `similarTo` is
        then putStrLn $ fn ++ " (" ++ descr ++ ") - pass"
        else putStrLn $ fn ++ " (" ++ descr ++ ") - fail - "++
            show (length sb)++" vs. "++show (length is)++" got "++show (zip is sb) 

similarTo :: [Float] -> [Float] -> Bool
similarTo xs ys = and (zipWith near xs ys) && length xs == length ys
  where
    near x y = abs (x - y) < 0.001

type Area = ((Int, Int), (Int, Int))
type Value = ((Int, Int), Float)
type Insertion = ((Int, Int), Float)
type Facet = (Int, Int, Int)

-- | Rasterize the specified facets, clipping to area.
-- Any gaps are filled with 0 values.
rasterize :: Area -> [Value] -> [Facet] -> [Float]
rasterize area@((x0,y0),(x1,y1)) values facets = toArray $ concatMap tri facets
  where
    tri :: (Int,Int,Int) -> [((Int, Int), Float)]
    tri (iA,iB,iC) =
        -- Sort by increasing X
        let [(aInt@(axInt, _), vA),
             (bInt,            vB),
             (cInt@(cxInt, _), vC)] = sortBy (comparing xCoord) $
                                                    map (values !!) [iA,iB,iC]
        -- Scan across
            a@(ax, _) = fromIntegral *** fromIntegral $ aInt :: (Float, Float)
            b@(bx, _) = fromIntegral *** fromIntegral $ bInt :: (Float, Float)
            c@(cx, _) = fromIntegral *** fromIntegral $ cInt :: (Float, Float)
        in  flip concatMap [(max x0 axInt)..(min x1 (cxInt-1))] $ \xInt ->
                let x = fromIntegral xInt :: Float
                    (yAC, vAC) = (interpolate a c x, interpolate (ax,vA) (cx,vC) x)
                    (yOther, vOther) = if x < bx
                                  then (interpolate a b x, interpolate (ax,vA) (bx,vB) x)
                                  else (interpolate b c x, interpolate (bx,vB) (cx,vC) x)
                    -- Order AC and Other so that y is increasing
                    (yBot, vBot, yTop, vTop) = if yAC <= yOther
                                  then (yAC, vAC, yOther, vOther)
                                  else (yOther, vOther, yAC, vAC)
                    yStart = max y0 (round yBot)
                    yEnd   = min y1 (round yTop - 1)
                    -- Displace odd columns because the terrain mesh is hexagonal
                    hexify y | even xInt = fromIntegral y
                    hexify y             = fromIntegral y + 0.5
                in  flip map [yStart .. yEnd] $ \yInt ->
                        ((xInt, yInt), interpolate (yBot, vBot) (yTop, vTop) (hexify yInt))
      where
        xCoord ((x, _), _) = x

    interpolate :: (Float, Float) -> (Float, Float) -> Float -> Float
    interpolate (t0, x0) (t1, x1) t = (t - t0) * (x1 - x0) / (t1 - t0) + x0

    toArray :: [Insertion] -> [Float]
    toArray output =
        let output' = A.accumArray (flip const) 0 area output :: Array (Int, Int) Float
        in  A.elems output'