module Waldo.Script (
Script(..)
, PanelSizes, PanelData(..), Panel(..)
, ImagePart(..)
, TextPart(..)
, Pos(..)
, loadImagePanels, mkScript, scriptName
) where
import Data.List
import Control.Monad
import Control.Monad.Reader
import qualified Data.Text as T
import qualified Data.Aeson as JS
import qualified Data.ByteString.Lazy.Char8 as BSL8
import Control.Monad.Trans.Resource (runResourceT)
import Data.Conduit (($$))
import qualified Data.Conduit.Binary as CB
import qualified Data.Conduit.ImageSize as CI
import System.FilePath ((</>), takeFileName, splitExtension)
import System.FilePath.Glob
import Data.Digest.Pure.SHA
import Control.DeepSeq
import System.Directory
import Data.Aeson ((.=))
pad :: Int
pad = 4
panelRightEdge :: Panel -> Int
panelRightEdge p = (pX $ pPos p) + (pWidth p)
scriptName :: Script -> T.Text
scriptName (s@Script {}) = T.concat $ [sName s, " : "] ++ (intersperse "+" $ map pName (sPanels s))
scriptName (ScriptTo goto) = T.concat ["Goto : ", goto]
mkScript :: T.Text
-> T.Text
-> [PanelData]
-> Script
mkScript nm alt pds =
let ps = snd $ mapAccumL (\xstart p ->
let newp = panelData2panel xstart p
in (panelRightEdge newp, newp)) 0 pds
in Script {
sAltText = alt
, sPanels = ps
, sHeight = 2*pad + (maximum $ map pHeight ps)
, sWidth = (1+length ps)*pad + (sum $ map pWidth ps)
, sName = nm
}
hashImgNm :: FilePath -> FilePath
hashImgNm fn =
let (nm, typ) = splitExtension fn
in (showDigest $ sha256 (BSL8.pack ("basfd" ++ nm)))++typ
loadImagePanels :: Int
-> Int
-> Int
-> ReaderT FilePath IO PanelSizes
loadImagePanels s p c = do
bp <- ask
fns <- liftIO $ glob (bp </> "panels" </>
("a1_"++show s++"p"++show p++"s*_"++show c++".*"))
ps <- liftIO $ forM fns $ \fn -> do
mImgInf <- runResourceT $ CB.sourceFile fn $$ CI.sinkImageSize
case mImgInf of
Nothing -> fail "Couldn't read image."
Just sz -> do
let pname = hashImgNm $ takeFileName fn
d <- BSL8.readFile fn
createDirectoryIfMissing False "/tmp/loadedPanels"
BSL8.writeFile ("/tmp/loadedPanels" </> pname) d
return $
PanelData {
pdWidth = CI.width sz
, pdHeight = CI.height sz
, pdImages = [ImagePart { ipPos = Pos 0 0, ipImageUrl = T.pack pname }]
, pdText = []
, pdName = T.pack ("p"++show p++"s"++show s++"_"++show c)
}
if null ps
then fail ("No panels found for "++show (s, p, c))
else return ps
panelData2panel :: Int -> PanelData -> Panel
panelData2panel xlast pd =
Panel {
pPos = Pos (xlast+pad) pad
, pWidth = pdWidth pd
, pHeight = pdHeight pd
, pImages = pdImages pd
, pText = pdText pd
, pName = pdName pd
}
type PanelSizes = [PanelData]
data Script =
ScriptTo {
sTarget :: !T.Text
}
| Script {
sWidth :: !Int
, sHeight :: !Int
, sAltText :: !T.Text
, sPanels :: [Panel]
, sName :: !T.Text
}
deriving (Eq, Ord, Show)
instance NFData Script where
rnf (s@ScriptTo {sTarget=t}) = t `seq` s `seq` ()
rnf (s@Script {sWidth=w, sHeight=h, sAltText=a, sPanels=p, sName=n}) =
w `seq` h `seq` a `deepseq` p `deepseq` n `deepseq` s `seq` ()
data Panel = Panel {
pPos :: !Pos
, pWidth :: !Int
, pHeight :: !Int
, pImages :: [ImagePart]
, pText :: [TextPart]
, pName :: !T.Text
} deriving (Eq, Ord, Show)
instance NFData Panel where
rnf (pan@Panel {pPos=p, pWidth=w, pHeight=h, pImages=i, pText=t, pName=n}) =
p `deepseq` w `seq` h `seq` i `deepseq` t `deepseq` n `deepseq` pan `seq` ()
data PanelData = PanelData {
pdWidth :: !Int
, pdHeight :: !Int
, pdImages :: [ImagePart]
, pdText :: [TextPart]
, pdName :: !T.Text
} deriving (Eq, Ord, Show)
data ImagePart = ImagePart {
ipPos :: !Pos
, ipImageUrl :: !T.Text
} deriving (Eq, Ord, Show)
instance NFData ImagePart where
rnf (i@ImagePart {ipPos=p, ipImageUrl=u}) =
p `deepseq` u `deepseq` i `seq` ()
data TextPart = TextPart {
tpPos :: !Pos
, tpString :: !T.Text
, tpSize :: !Float
, tpFont :: !T.Text
, tpAngle :: !Float
} deriving (Eq, Ord, Show)
instance NFData TextPart where
rnf (tp@TextPart {tpPos=p, tpString=t, tpSize=s, tpFont=f, tpAngle=a}) =
p `deepseq` t `deepseq` s `seq` f `deepseq` a `seq` tp `seq` ()
data Pos = Pos { pX :: !Int, pY :: !Int } deriving (Eq, Ord, Show)
instance NFData Pos where
rnf (p@Pos {pX=x, pY=y}) = x `seq` y `seq` p `seq` ()
instance JS.ToJSON Script where
toJSON (ScriptTo t) = JS.object ["goto" .= t]
toJSON (Script w h alt ps _) = JS.object [ "width" .= w
, "height" .= h
, "alttext" .= alt
, "panels" .= ps
]
instance JS.ToJSON Panel where
toJSON (Panel p w h is ts _) = JS.object [ "pos" .= p
, "width" .= w
, "height" .= h
, "images" .= is
, "texts" .= ts
]
instance JS.ToJSON ImagePart where
toJSON (ImagePart p url) = JS.object [ "pos" .= p, "url" .= url ]
instance JS.ToJSON TextPart where
toJSON (TextPart p str sz f r) = JS.object [ "pos" .= p
, "str" .= str
, "size" .= sz
, "font" .= f
, "rad" .= r
]
instance JS.ToJSON Pos where
toJSON (Pos x y) = JS.object [ "x" .= x, "y" .= y ]