summaryrefslogtreecommitdiff
path: root/src/System/Utility/H9Clock/Draw.hs
blob: 6f82564369ad1f1bdf3069b45f62a2e81c8b7cc2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
module System.Utility.H9Clock.Draw
        ( drawClock
        , mkGraphics
        , mkHandsPts
        , hourHandAngle
        , minHandAngle
        , dotsPts
        , darkBlue
        , paleBlue
        , paleBlueGreen
        )
where

import           Graphics.HGL
import           System.Utility.H9Clock.Type   as T
import           System.Utility.H9Clock.Time
import           Control.Monad
import           Control.Concurrent
import           Control.Concurrent.STM

mkHandsPts :: Rectangle -> Int -> Int -> ([Point], [Point])
mkHandsPts wRec hour min =
        let hourAng  = hourHandAngle hour min
            minAng   = minHandAngle min
            c        = center wRec
            hourHand = lineHourHandPts hourAng c wRec
            minHand  = lineMinHandPts minAng c wRec
        in (hourHand, minHand)

mkGraphics :: Rectangle -> Int -> Int -> (Graphic, Graphic, [Graphic])
mkGraphics wRec hour min = (hourHand, minHand, dots 4 4 wRec)
  where (hourHandPts, minHandPts) = mkHandsPts wRec hour min
        hourHand = polygon $ hourHandPts
        minHand = polygon $ minHandPts

hourHandAngle :: Integral a => a -> a -> a
hourHandAngle hour min = 90 - ((hour * 5) + (min `div` 10)) * 6

minHandAngle :: Num a => a -> a
minHandAngle min = 90 - (min * 6)

dotsPts :: Int -> Int -> Rectangle -> [(Point, Point)]
dotsPts w h r = ePts
    where
        c    = center r
        rad' = rad r
        degs = map ((360 `div` 12) *) [0 .. 11]
        cPts = map (circlePt c rad') degs
        ePts = map (ellipsePt w h) cPts

dots :: Int -> Int -> Rectangle -> [Graphic]
dots w h r = map (uncurry ellipse) $ dotsPts w h r

center :: Rectangle -> (Int, Int)
center r = divPt
    where
        addPt = (fst (T.min r) + fst (T.max r), snd (T.min r) + snd (T.max r))
        divPt = (fst addPt `div` 2, snd addPt `div` 2)

dx :: Rectangle -> Int
dx r = fst (T.max r) - fst (T.min r)

dy :: Rectangle -> Int
dy r = snd (T.max r) - snd (T.min r)

rad :: Rectangle -> Int
rad r = rad'
    where
        radD   = if dx r < dy r then dx r else dy r
        radDiv = radD `div` 2
        rad'   = radDiv - 8

circlePt :: (Int, Int) -> Int -> Int -> (Int, Int)
circlePt c r deg =
        let rad' = fromIntegral deg * (pi / 180.0)
            x    = fst c + round (cos rad' * fromIntegral r)
            y    = snd c - round (sin rad' * fromIntegral r)
        in  (x, y)

ellipsePt :: Int -> Int -> (Int, Int) -> (Point, Point)
ellipsePt a b c =
        let x = fst c
            y = snd c
        in  ((x - a, y + b), (x + a, y - b))

lineMinHandPts :: Int -> (Int, Int) -> Rectangle -> [Point]
lineMinHandPts ang c r =
        let
                rad' = (rad r * 3) `div` 4
                cPt  = circlePt c rad' ang
        in
          [ c
          , (fst c + 3  , snd c + 3)
          , (fst cPt + 3, snd cPt + 3)
          , cPt
          ]

lineHourHandPts :: Int -> (Int, Int) -> Rectangle -> [Point]
lineHourHandPts ang c r =
        let
                rad' = rad r `div` 2
                cPt  = circlePt c rad' ang
        in
          [ c
          , (fst c + 3  , snd c + 3)
          , (fst cPt + 3, snd cPt + 3)
          , cPt
          ]

darkBlue :: RGB
darkBlue = RGB 0 0 85

paleBlue :: RGB
paleBlue = RGB 0 0 187

paleBlueGreen :: RGB
paleBlueGreen = RGB 236 254 252

clockGraphic :: Rectangle -> (Int, Int) -> Int -> Int -> IO Graphic
clockGraphic rectangle wSize hour min = do
        let (hourHand, minHand, dots) = mkGraphics rectangle hour min
        return $ overGraphics
                [ withRGB darkBlue hourHand
                , withRGB paleBlue minHand
                , withColor Blue (overGraphics dots)
                , background
                ]
    where
        background =
                withRGB paleBlueGreen $ polygon
                        [(0, 0), (fst wSize, 0), wSize, (0, snd wSize)]

redraw :: Window -> (Int, Int) -> (Int, Int) -> IO ()
redraw w nSize (hour, min) = do g <- clockGraphic (Rectangle (0, 0) nSize) nSize hour min
                                setGraphic w g

milliSleep :: Int -> IO ()
milliSleep = threadDelay . (*) 1000

secSleep :: Int -> IO ()
secSleep = threadDelay . (*) 1000000

timeWorker :: Num a => TMVar a -> IO b
timeWorker var = loop
        where loop = secSleep 30 >> atomically (putTMVar var 0) >> loop

eventWorker :: Window -> TMVar Size -> Size -> IO ()
eventWorker w var wSize = loop
    where
        loop = do
                milliSleep 10
                me <- maybeGetWindowEvent w
                case me of
                        Just Resize -> do
                                size <- getWindowSize w
                                if wSize == size
                                        then loop
                                        else
                                                atomically (putTMVar var size)
                                                        >> loop
                        Just Closed -> closeWindow w
                        otherwise   -> loop

drawClock :: IO ()
drawClock = runGraphics $ do
        w <- openWindowEx "h9clock" Nothing windowSize DoubleBuffered (Just 20)
        (hour, min) <- getHourMin
        clock <- clockGraphic rectangle windowSize hour min
        setGraphic w clock
        t <- newEmptyTMVarIO
        e <- newEmptyTMVarIO
        forkIO $ timeWorker t
        forkIO $ eventWorker w e windowSize
        loop w windowSize t e (hour, min)
    where
        windowSize = (250, 250)
        rectangle  = Rectangle (0, 0) windowSize
        loop w wSize t e hourMin = do
                me <- atomically $ takeEitherTMVar e t
                curHour <- getHourMin
                case me of
                  Left size -> if curHour == hourMin && size == wSize
                               then loop w wSize t e hourMin
                               else redraw w size curHour >> loop w size t e curHour
                  Right _ -> if curHour == hourMin
                             then loop w wSize t e hourMin
                             else redraw w wSize curHour >> loop w wSize t e curHour

takeEitherTMVar :: TMVar a -> TMVar b -> STM (Either a b)
takeEitherTMVar ma mb =
        fmap Left (takeTMVar ma) `orElse` fmap Right (takeTMVar mb)