module Graphics.X11.TurtleMove (
Field,
Layer,
Character,
forkIOX,
openField,
closeField,
addLayer,
addCharacter,
fieldSize,
clearLayer,
clearCharacter,
addThread,
fieldColor,
onclick,
onrelease,
ondrag,
onkeypress,
waitField,
writeString,
moveTurtle
) where
import Graphics.X11.TurtleState(TurtleState(..), SVG(..), Position(..),
Color(..))
import Graphics.X11.TurtleField(
Field, Layer, Character,
forkIOX, openField, closeField, flushLayer,
addLayer, addCharacter, fieldSize, clearLayer,
drawLine, drawCharacter, drawCharacterAndLine, undoLayer,
drawLineNotFlush,
clearCharacter, addThread,
fieldColor, onkeypress, onclick, onrelease, ondrag, waitField, writeString
)
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
moveTurtle :: Character -> Layer -> TurtleState -> TurtleState -> IO ()
moveTurtle c l t0 t1 = do
when (undo t1 && (line t0 || isJust (draw t0))) $ do
done <- undoLayer l
unless done $ clearLayer l >> drawLines l (drawed t1)
when (undo t1 && clear t0) $ drawLines l $ drawed t1
when (visible t1) $ do
forM_ (getDirections (dir t0) (dir t1)) $ \d -> do
drawTurtle c (pencolor t1) (shape t1) (shapesize t1) d
(pensize t1) p0 Nothing
threadDelay rotateSpeed
forM_ (getPositions x0 y0 x1 y1) $ \p -> do
drawTurtle c (pencolor t1) (shape t1) (shapesize t1)
(dir t1) (pensize t1) p lineOrigin
threadDelay moveSpeed
drawTurtle c (pencolor t1) (shape t1) (shapesize t1) (dir t1)
(pensize t1) p1 lineOrigin
unless (visible t1) $ clearCharacter c
when (not (undo t1) && line t1) $
drawLine l (pensize t1) (pencolor t1) x0 y0 x1 y1 >> flushLayer l
when (clear t1) $ clearLayer l >> flushLayer l
unless (undo t1) $ drawDraw l (draw t1) >> flushLayer l
where
(tl, to) = if undo t1 then (t0, t1) else (t1, t0)
lineOrigin = if line tl then Just $ position to else Nothing
p0@(x0, y0) = position t0
p1@(x1, y1) = position t1
drawLines :: Layer -> [SVG] -> IO ()
drawLines l = mapM_ (drawDraw l . Just) . reverse
drawDraw :: Layer -> Maybe SVG -> IO ()
drawDraw _ Nothing = return ()
drawDraw l (Just (Line (Center x0 y0) (Center x1 y1) clr lw)) =
drawLineNotFlush l lw clr x0 y0 x1 y1
drawDraw l (Just (Text (Center x y) sz (RGB r_ g_ b_) fnt str)) =
writeString l fnt sz r g b x y str
where
[r, g, b] = map ((/ 0xff) . fromIntegral) [r_, g_, b_]
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 :: Character -> Color -> [Pos] -> Double -> Double -> Double ->
Pos -> Maybe Pos -> IO ()
drawTurtle c clr sh s d lw (px, py) org = do
let sp = map (((+ px) *** (+ py)) . rotatePoint . ((* s) *** (* s))) sh
maybe (drawCharacter c clr sp)
(\(x0, y0) -> (drawCharacterAndLine 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)