module Graphics.X11.CharAndBG (
Field,
Turtle,
openField,
newTurtle,
goto,
rotate,
direction,
position,
shape,
shapesize,
undo,
clear,
setUndoN,
windowWidth,
windowHeight,
penup,
pendown,
isdown,
testModuleCharAndBG
) where
import Graphics.X11.WindowLayers
import Control.Concurrent
import Data.IORef
import Control.Arrow
import Control.Monad
type Field = Win
type Turtle = Square
openField :: IO Field
openField = do
w <- openWin
flushWin w
return w
newTurtle :: Field -> IO Turtle
newTurtle f = do
s <- newSquare f
showSquare s
return s
goto :: Turtle -> Double -> Double -> IO ()
goto t x y = do
(width, height) <- winSize (sWin t)
moveSquare (sWin t) t (x + width / 2) ( y + height / 2)
rotate :: Turtle -> Double -> IO ()
rotate t = rotateSquare t . negate
direction :: Turtle -> IO Double
direction = fmap negate . readIORef . sDir
position :: Turtle -> IO (Double, Double)
position t = do
(x_, y_) <- readIORef $ sPos t
(width, height) <- winSize (sWin t)
return (x_ width / 2, y_ + height / 2)
undo, undoGen :: Turtle -> IO ()
undoGen t = do
rot : rots <- readIORef $ sRotHist t
writeIORef (sRotHist t) rots
d <- readIORef $ sDir t
case rot of
Just r -> rotateGen t (d r) >> return ()
Nothing -> undoSquare (sWin t) t
undo t = do
n <- readIORef $ sUndoN t
ns <- readIORef $ sUndoNs t
case ns of
n' : ns' -> do
writeIORef (sUndoN t) n'
writeIORef (sUndoNs t) ns'
_ -> writeIORef (sUndoN t) 1
print n
replicateM_ n $ undoGen t
setUndoN :: Turtle -> Int -> IO ()
setUndoN t n = do
n0 <- readIORef $ sUndoN t
writeIORef (sUndoN t) n
modifyIORef (sUndoNs t) (n0 :)
clear :: Turtle -> IO ()
clear Square{sWin = w, sLayer = l} = clearLayer w l
windowWidth, windowHeight :: Turtle -> IO Double
windowWidth = fmap fst . winSize . sWin
windowHeight = fmap snd . winSize . sWin
penup, pendown :: Turtle -> IO ()
penup = flip writeIORef False . sPenDown
pendown = flip writeIORef True . sPenDown
isdown :: Turtle -> IO Bool
isdown = readIORef . sPenDown
data Square = Square{
sLayer :: Layer,
sChar :: Character,
sPos :: IORef (Double, Double),
sHistory :: IORef [(Double, Double)],
sSize :: IORef Double,
sDir :: IORef Double,
sShape :: IORef [(Double, Double)],
sUndoN :: IORef Int,
sUndoNs :: IORef [Int],
sIsRotated :: IORef Bool,
sRotHist :: IORef [Maybe Double],
sPenDown :: IORef Bool,
sWin :: Win
}
testModuleCharAndBG :: IO ()
testModuleCharAndBG = main
main :: IO ()
main = do
w <- openWin
s <- newSquare w
s1 <- newSquare w
shape s1 "turtle"
shapesize s1 1
moveSquare w s 100 105
moveSquare w s1 200 30
moveSquare w s 50 300
moveSquare w s1 20 30
moveSquare w s 300 300
shapesize s1 2
undoSquare w s
moveSquare w s 300 400
rotateSquare s1 0
undoSquare w s
moveSquare w s 300 200
undoSquare w s
undoSquare w s1
undoSquare w s
getLine >> return ()
newSquare :: Win -> IO Square
newSquare w = do
l <- addLayer w
c <- addCharacter w
(width, height) <- winSize w
p <- newIORef (width / 2, height / 2)
h <- newIORef []
sr <- newIORef 1
dr <- newIORef 0
rsh <- newIORef classic
run <- newIORef 1
runs <- newIORef []
isr <- newIORef False
srh <- newIORef []
rpd <- newIORef True
return Square{
sLayer = l,
sChar = c,
sPos = p,
sHistory = h,
sSize = sr,
sWin = w,
sShape = rsh,
sDir = dr,
sUndoN = run,
sUndoNs = runs,
sIsRotated = isr,
sRotHist = srh,
sPenDown = rpd
}
shape :: Square -> String -> IO ()
shape s@Square{sShape = rsh} name =
case name of
"turtle" -> do
writeIORef rsh turtle
showSquare s
"clasic" -> do
writeIORef rsh classic
showSquare s
_ -> return ()
shapesize :: Square -> Double -> IO ()
shapesize s size = do
writeIORef (sSize s) size
p <- readIORef $ sPos s
uncurry (moveSquare (sWin s) s) p
step :: Double
step = 10
stepTime :: Int
stepTime = 10000
stepDir :: Double
stepDir = 5
stepDirTime :: Int
stepDirTime = 10000
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
showAnimation :: Bool -> Win -> Square -> Double -> Double -> Double -> Double -> IO ()
showAnimation pd w s x1 y1 x2 y2 = do
(size, d, sh) <- getSizeDirShape s
if pd then setPolygonCharacterAndLine w (sChar s)
(getShape sh size d x2 y2) (x1, y1) (x2, y2)
else setPolygonCharacter w (sChar s) (getShape sh size d x2 y2)
bufToWin w
flushWin w
getSizeDirShape :: Square -> IO (Double, Double, [(Double, Double)])
getSizeDirShape s = do
size <- readIORef (sSize s)
d <- readIORef (sDir s)
sh <- readIORef (sShape s)
return (size, d, sh)
showSquare :: Square -> IO ()
showSquare s@Square{sWin = w} = do
(x, y) <- readIORef $ sPos s
(size, d, sh) <- getSizeDirShape s
setPolygonCharacter w (sChar s) (getShape sh size d x y)
bufToWin w
flushWin w
moveSquare :: Win -> Square -> Double -> Double -> IO ()
moveSquare w s@Square{sPos = p} x2 y2 = do
modifyIORef (sRotHist s) (Nothing :)
writeIORef (sIsRotated s) False
(x1, y1) <- readIORef p
modifyIORef (sHistory s) ((x1, y1) :)
pd <- readIORef $ sPenDown s
mapM_ (\(x, y) -> showAnimation pd w s x1 y1 x y >> threadDelay stepTime) $
getPoints x1 y1 x2 y2
writeIORef p (x2, y2)
when pd $ line w (sLayer s) x1 y1 x2 y2
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
setDirSquare :: Square -> Double -> IO ()
setDirSquare s@Square{sDir = dr} d = do
writeIORef dr d
showSquare s
rotateSquare :: Square -> Double -> IO ()
rotateSquare s d = do
d0 <- rotateGen s d
modifyIORef (sRotHist s) (Just (d d0) :)
rotateGen :: Square -> Double -> IO Double
rotateGen s@Square{sDir = dr} d = do
d0 <- readIORef dr
mapM_ ((>> threadDelay stepDirTime) . setDirSquare s) $ getDirections d0 d
writeIORef dr (d `modd` 360)
return d0
modd :: (Num a, Ord a) => a -> a -> a
modd x y
| x < 0 = modd (x + y) y
| x < y = x
| otherwise = modd (x y) y
undoSquare :: Win -> Square -> IO ()
undoSquare w s@Square{sLayer = l} = do
undoLayer w l
(x1, y1) <- readIORef $ sPos s
p@(x2, y2) : ps <- readIORef $ sHistory s
mapM_ (\(x, y) -> showAnimation True w s x2 y2 x y >> threadDelay 50000) $
getPoints x1 y1 x2 y2
writeIORef (sPos s) p
writeIORef (sHistory s) ps
getShape ::
[(Double, Double)] -> Double -> Double -> Double -> Double -> [(Double, Double)]
getShape sh s d x y =
map (uncurry (addDoubles (x, y)) . rotatePointD d . mulPoint s) sh
classic :: [(Double, Double)]
classic = clssc ++ reverse (map (second negate) clssc)
where
clssc = [
( 10, 0),
( 16, 6),
(0, 0)
]
turtle :: [(Double, Double)]
turtle = ttl ++ reverse (map (second negate) ttl)
where
ttl = [
( 10, 0),
( 8, 3),
( 10, 5),
( 7, 9),
( 5, 6),
(0, 8),
(4, 7),
(6, 10),
(8, 7),
(7, 5),
(10, 2),
(13, 3),
(16, 0)
]
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)