module Graphics.X11.Turtle.Move (
Field,
Layer,
Character,
forkIOX,
openField,
closeField,
addLayer,
addCharacter,
fieldSize,
clearLayer,
clearCharacter,
addThread,
fieldColor,
onclick,
onrelease,
ondrag,
onkeypress,
waitField,
writeString,
moveTurtle
) where
import Graphics.X11.Turtle.State(TurtleState(..))
import Graphics.X11.Turtle.Field(
withLock2,
Field, Layer, Character,
forkIOX, openField, closeField, flushLayer,
addLayer, addCharacter, fieldSize, clearLayer,
drawCharacter, drawCharacterAndLine, undoLayer,
drawLine,
clearCharacter, addThread,
fieldColor, onkeypress, onclick, onrelease, ondrag, waitField, writeString
)
import Text.XML.YJSVG
import Control.Concurrent(threadDelay)
import Control.Monad(when, unless, forM_)
import Control.Arrow((***))
import Data.Maybe
type Pos = (Double, Double)
step :: Double
step = 10
moveSpeed :: Int
moveSpeed = 50000
stepDir :: Double
stepDir = 1 / 72
rotateSpeed :: Int
rotateSpeed = 10000
dir :: TurtleState -> Double
dir t = direction t / degrees t
lock :: Field -> IO a -> IO a
lock f = flip withLock2 f . const
moveTurtle :: Field -> Character -> Layer -> TurtleState -> TurtleState -> IO ()
moveTurtle f c l t0 t1 = do
when (undo t1 && clear t0) $ lock f $ drawLines f l $ drawed t1
when (undo t1 && isJust (draw t0)) $ lock f $ do
done <- undoLayer l
unless done $ clearLayer l >> drawLines f l (drawed t1)
drawTurtle f c (pencolor t1) (shape t1) (shapesize t1)
(dir t1) (pensize t1) (x0, y0) lineOrigin
when (visible t1) $ do
forM_ (getDirections (dir t0) (dir t1)) $ \d -> lock f $ do
drawTurtle f c (pencolor t1) (shape t1) (shapesize t1) d
(pensize t1) p0 Nothing
flushLayer f
threadDelay rotateSpeed
forM_ (getPositions x0 y0 x1 y1) $ \p -> lock f $ do
drawTurtle f c (pencolor t1) (shape t1) (shapesize t1)
(dir t1) (pensize t1) p lineOrigin
flushLayer f
threadDelay moveSpeed
lock f $ do
drawTurtle f c (pencolor t1) (shape t1) (shapesize t1) (dir t1)
(pensize t1) p1 lineOrigin
flushLayer f
unless (visible t1) $ clearCharacter f c
when (clear t1) $ lock f $ clearLayer l >> flushLayer f
unless (undo t1) $ lock f $ drawDraw f l (draw t1) >> flushLayer f
where
(tl, to) = if undo t1 then (t0, t1) else (t1, t0)
lineOrigin = if pendown tl then Just $ position to else Nothing
p0@(x0, y0) = position t0
p1@(x1, y1) = position t1
drawLines :: Field -> Layer -> [SVG] -> IO ()
drawLines f l = mapM_ (drawDraw f l . Just) . reverse
drawDraw :: Field -> Layer -> Maybe SVG -> IO ()
drawDraw _ _ Nothing = return ()
drawDraw f l (Just (Line (Center x0 y0) (Center x1 y1) clr lw)) =
drawLine f l lw clr x0 y0 x1 y1
drawDraw f l (Just (Text (Center x y) sz clr fnt str)) =
writeString f l fnt sz clr x y str
drawDraw _ _ _ = error "not implemented"
getPositions :: Double -> Double -> Double -> Double -> [Pos]
getPositions x0 y0 x1 y1 = take num $ zip [x0, x0 + dx .. ] [y0, y0 + dy .. ]
where
num = floor $ dist / step
dist = ((x1 x0) ** 2 + (y1 y0) ** 2) ** (1/2)
dx = step * (x1 x0) / dist
dy = step * (y1 y0) / dist
getDirections :: Double -> Double -> [Double]
getDirections ds de = [ds, ds + dd .. de dd]
where
dd = if de > ds then stepDir else stepDir
drawTurtle :: Field -> Character -> Color -> [Pos] -> Double -> Double -> Double ->
Pos -> Maybe Pos -> IO ()
drawTurtle f c clr sh s d lw (px, py) org = do
let sp = map (((+ px) *** (+ py)) . rotatePoint . ((* s) *** (* s))) sh
maybe (drawCharacter f c clr sp)
(\(x0, y0) -> (drawCharacterAndLine f c clr sp lw x0 y0 px py)) org
where
rotatePoint (x, y) = let rad = d * 2 * pi in
(x * cos rad y * sin rad, x * sin rad + y * cos rad)