mirror of
https://github.com/reanimate/reanimate.git
synced 2026-09-10 23:52:22 +00:00
Add voice control via Reanimate.Voice. Include rtfd article. Former-commit-id: f80bb58031f6dfbb83bc8f188c17013afcb2dfd9
153 lines
4.9 KiB
Haskell
153 lines
4.9 KiB
Haskell
#!/usr/bin/env stack
|
|
-- stack --resolver lts-15.04 runghc --package reanimate
|
|
{-# LANGUAGE OverloadedStrings #-}
|
|
{-# LANGUAGE ApplicativeDo #-}
|
|
module Main where
|
|
|
|
import Control.Monad
|
|
import qualified Data.Text as T
|
|
import Reanimate
|
|
import Reanimate.Ease
|
|
import Reanimate.Voice
|
|
import Reanimate.Builtin.Documentation
|
|
import Geom2D.CubicBezier ( QuadBezier(..)
|
|
, evalBezier
|
|
, Point(..)
|
|
)
|
|
import Graphics.SvgTree ( ElementRef(..) )
|
|
|
|
transcript :: Transcript
|
|
transcript = loadTranscript "voice_advanced.txt"
|
|
|
|
main :: IO ()
|
|
main = reanimate $ sceneAnimation $ do
|
|
bg <- newSpriteSVG $ mkBackgroundPixel rtfdBackgroundColor
|
|
spriteZ bg (-100)
|
|
newSpriteSVG_ $ mkGroup
|
|
[withStrokeColor "black" $ mkLine (-screenWidth, 0) (screenWidth, 0)]
|
|
|
|
centerTxt <- textHandler
|
|
|
|
flashEffect
|
|
circleEffect
|
|
squareEffect
|
|
finalEffect
|
|
|
|
waitOn $ forM_ (transcriptWords transcript) $ \tword -> fork $ do
|
|
wait (wordStart tword)
|
|
writeVar centerTxt $ wordReference tword
|
|
|
|
wait 2
|
|
|
|
wordDuration :: TWord -> Double
|
|
wordDuration tword = wordEnd tword - wordStart tword
|
|
|
|
--
|
|
finalEffect :: Scene s ()
|
|
finalEffect = fork $ do
|
|
let begin = findWord transcript ["final"] "circles"
|
|
ends = findWords transcript ["final"] "flash"
|
|
path = QuadBezier (Point 6 (-radius)) (Point 0 6) (Point (-6) (-radius))
|
|
radius = 0.3
|
|
wait (wordStart begin)
|
|
ss <- fork $ replicateM 3 (circleSprite radius path <* wait 0.2)
|
|
mapM_ (flip spriteZ (-1)) ss
|
|
forM_ (zip ss ends) $ \(s, end) -> fork $ do
|
|
spriteMap s flipXAxis
|
|
wait (wordStart end - wordStart begin)
|
|
destroySprite s
|
|
|
|
-- square effect
|
|
squareEffect :: Scene s ()
|
|
squareEffect = fork $ do
|
|
let
|
|
begin = findWord transcript [] "square"
|
|
end = findWord transcript ["middle"] "square"
|
|
path =
|
|
QuadBezier (Point 6 (-size / 2)) (Point 0 6) (Point (-6) (-size / 2))
|
|
size = 1
|
|
wait (wordStart begin)
|
|
s <- squareSprite size path
|
|
spriteMap s (rotate 180)
|
|
spriteZ s (-1)
|
|
wait (wordStart end + wordDuration end / 2 - wordStart begin)
|
|
destroySprite s
|
|
|
|
-- circle effect
|
|
circleEffect :: Scene s ()
|
|
circleEffect = fork $ do
|
|
let begin = findWord transcript [] "circle"
|
|
end = findWord transcript ["middle"] "circle"
|
|
path = QuadBezier (Point 6 (-radius)) (Point 0 6) (Point (-6) (-radius))
|
|
radius = 0.3
|
|
wait (wordStart begin)
|
|
s <- circleSprite radius path
|
|
spriteZ s (-1)
|
|
wait (wordStart end + wordDuration end / 2 - wordStart begin)
|
|
destroySprite s
|
|
|
|
-- flash effect
|
|
flashEffect :: Scene s ()
|
|
flashEffect = forM_ (findWords transcript [] "flash") $ \flashWord -> fork $ do
|
|
wait (wordStart flashWord)
|
|
flash <- newSpriteSVG $ mkBackground "black"
|
|
spriteTween flash (wordDuration flashWord)
|
|
$ \t -> withGroupOpacity (fromToS 0 0.7 $ (powerS 2 . reverseS) t)
|
|
wait (wordDuration flashWord)
|
|
destroySprite flash
|
|
|
|
--------------------------------------------------------------------------
|
|
-- Helpers and sprites
|
|
|
|
textHandler :: Scene s (Var s T.Text)
|
|
textHandler = simpleVar render T.empty
|
|
where
|
|
render txt =
|
|
let txtSvg = translate 0 (-0.25) $ centerX $ latex txt
|
|
activeWidth = svgWidth txtSvg + 0.5
|
|
in mkGroup
|
|
[ withStrokeWidth 0 $ withFillColorPixel rtfdBackgroundColor $ mkRect
|
|
activeWidth
|
|
1
|
|
, txtSvg
|
|
, withStrokeColor "black"
|
|
$ mkLine (activeWidth / 2, 0.5) (activeWidth / 2, -0.5)
|
|
, withStrokeColor "black"
|
|
$ mkLine (-activeWidth / 2, 0.5) (-activeWidth / 2, -0.5)
|
|
]
|
|
|
|
circleSprite :: Double -> QuadBezier Double -> Scene s (Sprite s)
|
|
circleSprite radius path = newSprite $ do
|
|
t <- spriteT
|
|
d <- spriteDuration
|
|
pure
|
|
$ let Point x y = evalBezier path (t / d)
|
|
in mkGroup
|
|
[ mkClipPath "circle-mask"
|
|
$ removeGroups
|
|
$ translate 0 (screenHeight / 2)
|
|
$ withFillColorPixel rtfdBackgroundColor
|
|
$ mkRect screenWidth screenHeight
|
|
, withClipPathRef (Ref "circle-mask") $ translate x y $ mkCircle
|
|
radius
|
|
]
|
|
|
|
squareSprite :: Double -> QuadBezier Double -> Scene s (Sprite s)
|
|
squareSprite size path = newSprite $ do
|
|
t <- spriteT
|
|
d <- spriteDuration
|
|
pure
|
|
$ let Point x y = evalBezier path (t / d)
|
|
in mkGroup
|
|
[ mkClipPath "square-mask"
|
|
$ removeGroups
|
|
$ translate 0 (screenHeight / 2)
|
|
$ withFillColorPixel rtfdBackgroundColor
|
|
$ mkRect screenWidth screenHeight
|
|
, withClipPathRef (Ref "square-mask")
|
|
$ translate x y
|
|
$ rotate (t / d * 360)
|
|
$ withFillOpacity 0
|
|
$ withStrokeColor "black"
|
|
$ mkRect size size
|
|
]
|