module Language.Mecha.Assembly ( Asm , part , assemble , Scene , Camera (..) , view , animate ) where import Control.Monad import qualified Data.ByteString.Char8 as BS import Data.Digest.CRC32 import Data.List import Language.Mecha.Solid import Language.Mecha.Types import System.Directory import System.IO import System.Process import Text.Printf -- | The Asm holds all the parts and sub-assemblies. newtype Asm = Asm [Solid] deriving Eq instance Colorable Asm where color c (Asm a) = Asm $ map (color c) a instance Moveable Asm where move a (Asm b) = Asm $ map (move a) b rotateX a (Asm b) = Asm $ map (rotateX a) b rotateY a (Asm b) = Asm $ map (rotateY a) b rotateZ a (Asm b) = Asm $ map (rotateZ a) b instance Scaleable Asm where scale v (Asm a) = Asm $ map (scale v) a -- | Place a part (Solid) in an assembly. part :: Solid -> Asm part a = Asm [a] -- | Assemble multiple sub-assemblies together. assemble :: [Asm] -> Asm assemble a = Asm $ concat [ a | Asm a <- a ] -- | A Scene is a light position, camera configuration, and an assembly. type Scene = (Camera, Asm) -- | Defines a camera configuration. data Camera = Orthographic -- ^ Orthographgic projection at the origin with a radius. | Perspective -- ^ Perspective projection given a camera location and a target. deriving Eq -- | Renders 3 orthographic views and 1 perspective view and creates a little html page or the images. Assembly should be within 1 of origin. view :: FilePath -> Int -> Int -> Asm -> IO () view f h w a = do writeFile (f ++ ".html") $ unlines [ printf "" , printf "\n" f f , printf "\n" f f , printf "
" ] render (f ++ "Top") h w Orthographic $ rotateX (pi/2) a render (f ++ "Front") h w Orthographic $ a render (f ++ "Right") h w Orthographic $ rotateZ (-pi/2) a render (f ++ "Persp") h w Perspective $ moveY 1 $ rotateX (pi/4) $ rotateZ (-pi/6) a -- | Renders a MPEG movie with POVRay and ffmpeg given a file name (w/o file extension), heigth, width, frames-per-second, and a list of scenes. animate :: FilePath -> Int -> Int -> Int -> [Scene] -> IO () animate file h w fps scenes = do sequence_ [ printf "[ %d of %d ]\n" i n >> render (printf "%s%05d" file i) h w camera asm | (i, (camera, asm)) <- zip [1 .. n] scenes ] rm $ file ++ ".mpg" readProcess "ffmpeg" ["-sameq", "-i", file ++ "%05d.png", "-r", show fps, file ++ ".mpg"] "" sequence_ [ rm $ printf "%s%05d.png" file i | i <- [1 .. n] ] where n = length scenes -- | Renders a scene. render :: String -> Int -> Int -> Camera -> Asm -> IO () render file h w camera (Asm a) = do ln image link a <- doesFileExist image when (not a) $ do writeFile (file ++ ".pov") povray readProcess "povray" ["-D", "-V", "+H" ++ show h, "+W" ++ show w, "+I" ++ file ++ ".pov", "+O" ++ image] "" --rm $ file ++ ".pov" return () where checksum = printf "%08X" $ crc32 $ BS.pack $ show (h, w, povray) image = checksum ++ ".png" link = file ++ ".png" r :: Double r = fromIntegral w / fromIntegral h povray :: String povray = unlines [ "#include \"colors.inc\"" , "background { color White }" , printf "light_source { <100, 100, -100> color White }" , case camera of Perspective -> printf "camera { perspective location <0, 0, 0> right x*%f direction <0, 0, 1> }" r Orthographic -> printf "camera { orthographic location <0,0,-100> up y*1 right x*%f }" r ] ++ concatMap show a rm :: FilePath -> IO () rm f = system ("rm -f " ++ f) >> return () ln :: FilePath -> FilePath -> IO () ln a b = system ("ln -f -s " ++ a ++ " " ++ b) >> return ()