module Graphics.X11.TurtleDraw (
Field,
Layer,
Character,
openField,
addLayer,
addCharacter,
clearLayer,
turtleDraw,
winSize
) where
import Graphics.X11.TurtleState
import Control.Concurrent
import Control.Monad
import Graphics.X11.WindowLayers
turtleDraw, turtleDrawNotUndo, turtleDrawUndo ::
Field -> Character -> Layer -> TurtleState -> TurtleState -> IO ()
turtleDraw f c l t0 t1 = do
let isUndo = turtleUndo t1
if isUndo then turtleDrawUndo f c l t0 t1
else turtleDrawNotUndo f c l t0 t1
turtleDrawUndo f c l t0 t1 = do
let shape = turtleShape t1
size = turtleSize t1
prePos@(px, py) = turtlePos t0
preDir = turtleDir t0
pen = turtlePenDown t1
pos@(nx, ny) = turtlePos t1
dir = turtleDir t1
when pen $ undoLayer f l
forM_ (getDirections preDir dir) $ \d -> do
drawTurtle f c shape size d prePos Nothing
threadDelay 10000
if pen then forM_ (getPoints px py nx ny) $ \p -> do
drawTurtle f c shape size dir p $ Just pos
threadDelay 50000
else forM_ (getPoints px py nx ny) $ \p -> do
drawTurtle f c shape size dir p Nothing
threadDelay 50000
turtleDrawNotUndo f c l t0 t1 = do
let shape = turtleShape t1
size = turtleSize t1
prePos@(px, py) = turtlePos t0
prePen = turtlePenDown t0
preDir = turtleDir t0
(nx, ny) = turtlePos t1
dir = turtleDir t1
forM_ (getDirections preDir dir) $ \d ->
drawTurtle f c shape size d prePos Nothing
if prePen then do
forM_ (getPoints px py nx ny) $ \p -> do
drawTurtle f c shape size dir p $ Just prePos
threadDelay 50000
line f l px py nx ny
else forM_ (getPoints px py nx ny) $ \p -> do
drawTurtle f c shape size dir p Nothing
threadDelay 50000
step :: Double
step = 10
stepDir :: Double
stepDir = 5
getPoints :: Double -> Double -> Double -> Double -> [(Double, Double)]
getPoints x1 y1 x2 y2 = let
len = ((x2 x1) ** 2 + (y2 y1) ** 2) ** (1/2)
dx = (x2 x1) * step / len
dy = (y2 y1) * step / len in
zip (takeWhile (before dx x2) [x1, x1 + dx ..])
(takeWhile (before dy y2) [y1, y1 + dy ..]) ++
[(x2, y2)]
before :: (Num a, Ord a) => a -> a -> a -> Bool
before d t x = signum d * t >= signum d * x
getDirections :: Double -> Double -> [Double]
getDirections ds de = takeWhile beforeDir [ds, ds + dd ..] ++ [de]
where
sig = signum (de ds)
dd = sig * stepDir
beforeDir x = sig * x < sig * de
drawTurtle :: Field -> Character -> [(Double, Double)] -> Double -> Double ->
(Double, Double) -> Maybe (Double, Double) -> IO ()
drawTurtle w c sh s d (x, y) org = do
let sp = mkShape sh s d x y
maybe (setPolygonCharacter w c sp)
(flip (setPolygonCharacterAndLine w c sp) (x, y)) org
bufToWin w
flushWin w
mkShape ::
[(Double, Double)] -> Double -> Double -> Double -> Double -> [(Double, Double)]
mkShape sh s d x y =
map (uncurry (addDoubles (x, y)) . rotatePointD d . mulPoint s) sh
addDoubles :: (Double, Double) -> Double -> Double -> (Double, Double)
addDoubles (x, y) dx dy = (x + dx, y + dy)
rotatePointD :: Double -> (Double, Double) -> (Double, Double)
rotatePointD = rotatePointR . (* pi) . (/ 180)
rotatePointR :: Double -> (Double, Double) -> (Double, Double)
rotatePointR rad (x, y) =
(x * cos rad y * sin rad, x * sin rad + y * cos rad)
mulPoint :: Double -> (Double, Double) -> (Double, Double)
mulPoint s (x, y) = (x * s, y * s)