|
| 1 | +import Freenect |
| 2 | +import Data.Vector.Storable as V hiding ((++), length, foldr) |
| 3 | +import qualified Data.Vector.Storable as V |
| 4 | +import Control.Applicative |
| 5 | +import Control.Concurrent |
| 6 | +import Control.Monad |
| 7 | +import Graphics.UI.SDL as SDL |
| 8 | +import Graphics.UI.SDL.Primitives as SDL |
| 9 | +import Data.IORef |
| 10 | +import Data.Word |
| 11 | +import Data.Maybe |
| 12 | + |
| 13 | +-- TODO Use these instead of hard-coded values |
| 14 | +kinectWidth, kinectHeight :: Int |
| 15 | +kinectWidth = 640 |
| 16 | +kinectHeight = 480 |
| 17 | + |
| 18 | +type KinectPosRef = IORef KinectPos |
| 19 | +type KinectPos = Maybe (Double, Double) |
| 20 | + |
| 21 | +initializeKinect :: (Double, Double) -> IO KinectPosRef |
| 22 | +initializeKinect screenSize = do |
| 23 | + lastPos <- newIORef Nothing |
| 24 | + _ <- getDepthThread screenSize lastPos |
| 25 | + return lastPos |
| 26 | + |
| 27 | +getDepthThread :: (Double, Double) -> KinectPosRef -> IO ThreadId |
| 28 | +getDepthThread screenSize lastPos = forkIO $ do |
| 29 | + withContext $ \context -> do |
| 30 | + setLogLevel LogFatal context |
| 31 | + selectSubdevices context devices |
| 32 | + withDevice context index $ \device -> do |
| 33 | + setDepthMode device Medium ElevenBit |
| 34 | + setDepthCallback device $ \payload _timestamp -> do |
| 35 | + maybe (print ".") -- Too far or too close |
| 36 | + (updatePos lastPos) |
| 37 | + (calculateMousePos screenSize payload) |
| 38 | + return () |
| 39 | + startDepth device |
| 40 | + forever $ processEvents context |
| 41 | + |
| 42 | + where devices = [Camera] |
| 43 | + index = 0 :: Integer |
| 44 | + |
| 45 | +updatePos :: IORef (Maybe (Double, Double)) -> (Double, Double) -> IO () |
| 46 | +updatePos lastPosRef newPos@(nx,ny) = do |
| 47 | + lastPosM <- readIORef lastPosRef |
| 48 | + let (mx, my) = case lastPosM of |
| 49 | + Nothing -> newPos |
| 50 | + (Just (lx,ly)) -> (adjust 50 lx nx, adjust 50 ly ny) |
| 51 | + writeIORef lastPosRef (Just (mx, my)) |
| 52 | + mx `seq` my `seq` return () |
| 53 | + |
| 54 | +calculateMousePos :: (Double, Double) -> Vector Word16 -> Maybe (Double, Double) |
| 55 | +calculateMousePos (width, height) payload = |
| 56 | + fmap g (findFirst payload) |
| 57 | + where g (px,py) = (mousex, mousey) |
| 58 | + where |
| 59 | + pointerx = fromIntegral (640 - px) |
| 60 | + pointery = fromIntegral py |
| 61 | + mousex = pointerx * adjx |
| 62 | + mousey = pointery * adjy |
| 63 | + adjx = width / 630.0 |
| 64 | + adjy = height / 470.0 |
| 65 | + |
| 66 | +mat :: Vector Float |
| 67 | +mat = V.generate 2048 (\i -> let v :: Float |
| 68 | + v = ((fromIntegral i/2048.0)^3)*6.0 in v * 6.0 * 256.0) |
| 69 | + |
| 70 | +findFirst :: Vector Word16 -> Maybe (Int, Int) |
| 71 | +findFirst vs = fmap (\v -> (v `mod` 640, v `div` 640)) i |
| 72 | + where i = V.findIndex (\x -> mat!(fromIntegral x) < 512) vs |
| 73 | + |
| 74 | +processPayload :: Vector Word16 -> [(Float, Int, Int)] |
| 75 | +processPayload ps = [(pval, tx, ty) | i <- [0..640*480-1] |
| 76 | + , let pval = mat!(fromIntegral (ps!i)) |
| 77 | + , pval < 300 |
| 78 | + , let ty = i `div` 640 |
| 79 | + tx = i `mod` 640 |
| 80 | + ] |
| 81 | + |
| 82 | +-- Drop the fst elem, calculate the avg of snd and trd over the whole list |
| 83 | +avg :: [(Float, Int, Int)] -> (Int, Int) |
| 84 | +avg ls = (sumx `div` l, sumy `div` l) |
| 85 | + where l = length ls |
| 86 | + (sumx, sumy) = foldr (\(_,x,y) (rx,ry) -> (x+rx,y+ry)) (0,0) ls |
| 87 | + |
| 88 | +-- Update a value, with a max cap |
| 89 | +adjust :: (Num a, Ord a) => a -> a -> a -> a |
| 90 | +adjust maxD old new |
| 91 | + | abs (old - new) < maxD = new |
| 92 | + | old < new = old + maxD |
| 93 | + | otherwise = old - maxD |
| 94 | + |
| 95 | +main :: IO () |
| 96 | +main = do |
| 97 | + SDL.init [InitVideo] |
| 98 | + |
| 99 | + screen <- SDL.setVideoMode width height 32 [SWSurface] |
| 100 | + |
| 101 | + putStrLn "Initializing Kinect." |
| 102 | + |
| 103 | + kinectPos <- initializeKinect (width, height) |
| 104 | + |
| 105 | + -- NEW |
| 106 | + t <- SDL.getTicks |
| 107 | + fpsCounter <- newIORef (0, t) |
| 108 | + |
| 109 | + -- "game" loop |
| 110 | + forever $ do |
| 111 | + |
| 112 | + -- Render |
| 113 | + let format = surfaceGetPixelFormat screen |
| 114 | + white <- mapRGB format 255 255 255 |
| 115 | + fillRect screen Nothing white |
| 116 | + |
| 117 | + t' <- SDL.getTicks |
| 118 | + (n,t) <- readIORef fpsCounter |
| 119 | + let td = t' - t |
| 120 | + let tpf = fromIntegral td / fromIntegral n / 1000 |
| 121 | + |
| 122 | + if td > 1000 |
| 123 | + then do putStrLn $ "Time per frame (in seconds): " ++ show tpf |
| 124 | + putStrLn $ "FPS: " ++ show (1.0 / tpf) |
| 125 | + writeIORef fpsCounter (0, t') |
| 126 | + else writeIORef fpsCounter (n + 1, t) |
| 127 | + |
| 128 | + (x,y) <- fromMaybe (width / 2, height / 2) <$> readIORef kinectPos |
| 129 | + |
| 130 | + let color = Pixel 0xFF0000FF |
| 131 | + x' = round x |
| 132 | + y' = round y |
| 133 | + filledCircle screen x' y' 30 color |
| 134 | + |
| 135 | + SDL.flip screen |
| 136 | + |
| 137 | +width :: Num a => a |
| 138 | +width = 1024 |
| 139 | + |
| 140 | +height :: Num a => a |
| 141 | +height = 768 |
| 142 | + |
0 commit comments