module Graphics.Blank.Canvas where
import Control.Monad (ap, liftM2)
import Data.Aeson (FromJSON(..),Value(..),encode)
import Data.Aeson.Types (Parser, (.:))
import Data.Monoid
import Data.Text (Text)
import Data.Text.Lazy.Builder
import Data.Text.Lazy.Encoding (decodeUtf8)
import Graphics.Blank.Events
import Graphics.Blank.JavaScript
import Graphics.Blank.Types
import Graphics.Blank.Types.Cursor
import Graphics.Blank.Types.Font
import Prelude.Compat
import TextShow
import TextShow.TH (deriveTextShow)
data DeviceAttributes = DeviceAttributes Int Int Double deriving Show
$(deriveTextShow ''DeviceAttributes)
data TextMetrics = TextMetrics Double deriving Show
$(deriveTextShow ''TextMetrics)
data Canvas :: * -> * where
Method :: Method -> Canvas ()
Command :: Command -> Canvas ()
Function :: TextShow a => Function a -> Canvas a
Query :: TextShow a => Query a -> Canvas a
With :: CanvasContext -> Canvas a -> Canvas a
MyContext :: Canvas CanvasContext
Bind :: Canvas a -> (a -> Canvas b) -> Canvas b
Return :: a -> Canvas a
instance Monad Canvas where
return = Return
(>>=) = Bind
instance Applicative Canvas where
pure = return
(<*>) = ap
instance Functor Canvas where
fmap f c = c >>= return . f
instance Monoid a => Monoid (Canvas a) where
mappend = liftM2 mappend
mempty = return mempty
data Method
= Arc (Double, Double, Double, Radians, Radians, Bool)
| ArcTo (Double, Double, Double, Double, Double)
| BeginPath
| BezierCurveTo (Double, Double, Double, Double, Double, Double)
| ClearRect (Double, Double, Double, Double)
| Clip
| ClosePath
| forall image . Image image => DrawImage (image,[Double])
| Fill
| FillRect (Double, Double, Double, Double)
| forall style . Style style => FillStyle style
| FillText (Text, Double, Double)
| forall canvasFont . CanvasFont canvasFont => Font canvasFont
| GlobalAlpha Alpha
| GlobalCompositeOperation Text
| LineCap LineEndCap
| LineJoin LineJoinCorner
| LineTo (Double, Double)
| LineWidth Double
| MiterLimit Double
| MoveTo (Double, Double)
| PutImageData (ImageData, [Double])
| QuadraticCurveTo (Double, Double, Double, Double)
| Rect (Double, Double, Double, Double)
| Restore
| Rotate Radians
| Save
| Scale (Interval, Interval)
| SetTransform (Double, Double, Double, Double, Double, Double)
| ShadowBlur Double
| forall canvasColor . CanvasColor canvasColor => ShadowColor canvasColor
| ShadowOffsetX Double
| ShadowOffsetY Double
| Stroke
| StrokeRect (Double, Double, Double, Double)
| forall style . Style style => StrokeStyle style
| StrokeText (Text,Double, Double)
| TextAlign TextAnchorAlignment
| TextBaseline TextBaselineAlignment
| Transform (Double, Double, Double, Double, Double, Double)
| Translate (Double, Double)
data Command
= Trigger Event
| forall color . CanvasColor color => AddColorStop (Interval, color) CanvasGradient
| forall msg . JSArg msg => Log msg
| Eval Text
instance Show Command where
showsPrec p = showsPrec p . FromTextShow
instance TextShow Command where
showb (Trigger e) = "Trigger(" <> (fromLazyText . decodeUtf8 $ encode e) <> singleton ')'
showb (AddColorStop (off,rep) g) = jsCanvasGradient g <> ".addColorStop("
<> jsDouble off <> singleton ',' <> jsCanvasColor rep
<> singleton ')'
showb (Log msg) = "console.log(" <> showbJS msg <> singleton ')'
showb (Eval cmd) = fromText cmd
with :: CanvasContext -> Canvas a -> Canvas a
with = With
myCanvasContext :: Canvas CanvasContext
myCanvasContext = MyContext
trigger :: Event -> Canvas ()
trigger = Command . Trigger
addColorStop :: CanvasColor color => (Interval, color) -> CanvasGradient -> Canvas ()
addColorStop (off,rep) = Command . AddColorStop (off,rep)
console_log :: JSArg msg => msg -> Canvas ()
console_log = Command . Log
eval :: Text -> Canvas ()
eval = Command . Eval
data Function :: * -> * where
CreateLinearGradient :: (Double,Double,Double,Double) -> Function CanvasGradient
CreateRadialGradient :: (Double,Double,Double,Double,Double,Double) -> Function CanvasGradient
CreatePattern :: Image image => (image, RepeatDirection) -> Function CanvasPattern
instance Show (Function a) where
showsPrec p = showsPrec p . FromTextShow
instance TextShow (Function a) where
showb (CreateLinearGradient (x0,y0,x1,y1)) = "createLinearGradient("
<> jsDouble x0 <> singleton ',' <> jsDouble y0 <> singleton ','
<> jsDouble x1 <> singleton ',' <> jsDouble y1 <> singleton ')'
showb (CreateRadialGradient (x0,y0,r0,x1,y1,r1)) = "createRadialGradient("
<> jsDouble x0 <> singleton ',' <> jsDouble y0 <> singleton ',' <> jsDouble r0 <> singleton ','
<> jsDouble x1 <> singleton ',' <> jsDouble y1 <> singleton ',' <> jsDouble r1 <> singleton ')'
showb (CreatePattern (img,dir)) = "createPattern("
<> jsImage img <> singleton ',' <> jsRepeatDirection dir <> singleton ')'
data Query :: * -> * where
Device :: Query DeviceAttributes
ToDataURL :: Query Text
MeasureText :: Text -> Query TextMetrics
IsPointInPath :: (Double, Double) -> Query Bool
NewImage :: Text -> Query CanvasImage
NewAudio :: Text -> Query CanvasAudio
NewCanvas :: (Int, Int) -> Query CanvasContext
GetImageData :: (Double, Double, Double, Double) -> Query ImageData
Cursor :: CanvasCursor cursor => cursor -> Query ()
Sync :: Query ()
instance Show (Query a) where
showsPrec p = showsPrec p . FromTextShow
instance TextShow (Query a) where
showb Device = "Device"
showb ToDataURL = "ToDataURL"
showb (MeasureText txt) = "MeasureText(" <> jsText txt <> singleton ')'
showb (IsPointInPath (x,y)) = "IsPointInPath(" <> jsDouble x <> singleton ','
<> jsDouble y <> singleton ')'
showb (NewImage url') = "NewImage(" <> jsText url' <> singleton ')'
showb (NewAudio txt) = "NewAudio(" <> jsText txt <> singleton ')'
showb (NewCanvas (x,y)) = "NewCanvas(" <> jsInt x <> singleton ','
<> jsInt y <> singleton ')'
showb (GetImageData (sx,sy,sw,sh)) = "GetImageData(" <> jsDouble sx <> singleton ','
<> jsDouble sy <> singleton ','
<> jsDouble sw <> singleton ','
<> jsDouble sh <> singleton ')'
showb (Cursor cur) = "Cursor(" <> jsCanvasCursor cur <> singleton ')'
showb Sync = "Sync"
parseQueryResult :: Query a -> Value -> Parser a
parseQueryResult (Device {}) o = uncurry3 DeviceAttributes <$> parseJSON o
parseQueryResult (ToDataURL {}) o = parseJSON o
parseQueryResult (MeasureText {}) (Object v) = TextMetrics <$> v .: "width"
parseQueryResult (IsPointInPath {}) o = parseJSON o
parseQueryResult (NewImage {}) o = uncurry3 CanvasImage <$> parseJSON o
parseQueryResult (NewAudio {}) o = uncurry CanvasAudio <$> parseJSON o
parseQueryResult (NewCanvas {}) o = uncurry3 CanvasContext <$> parseJSON o
parseQueryResult (GetImageData {}) (Object o) = ImageData
<$> (o .: "width")
<*> (o .: "height")
<*> (o .: "data")
parseQueryResult (Cursor {}) _ = return ()
parseQueryResult (Sync {}) _ = return ()
parseQueryResult _ _ = fail "no parse in blank-canvas server (internal error)"
uncurry3 :: (a -> b -> c -> d) -> (a, b, c) -> d
uncurry3 f (a,b,c) = f a b c
device :: Canvas DeviceAttributes
device = Query Device
toDataURL :: () -> Canvas Text
toDataURL () = Query ToDataURL
measureText :: Text -> Canvas TextMetrics
measureText = Query . MeasureText
isPointInPath :: (Double, Double) -> Canvas Bool
isPointInPath = Query . IsPointInPath
newImage :: Text -> Canvas CanvasImage
newImage = Query . NewImage
newAudio :: Text -> Canvas CanvasAudio
newAudio = Query . NewAudio
createLinearGradient :: (Double, Double, Double, Double) -> Canvas CanvasGradient
createLinearGradient = Function . CreateLinearGradient
createRadialGradient :: (Double, Double, Double, Double, Double, Double) -> Canvas CanvasGradient
createRadialGradient = Function . CreateRadialGradient
createPattern :: (CanvasImage, RepeatDirection) -> Canvas CanvasPattern
createPattern = Function . CreatePattern
newCanvas :: (Int, Int) -> Canvas CanvasContext
newCanvas = Query . NewCanvas
getImageData :: (Double, Double, Double, Double) -> Canvas ImageData
getImageData = Query . GetImageData
cursor :: CanvasCursor cursor => cursor -> Canvas ()
cursor = Query . Cursor
sync :: Canvas ()
sync = Query Sync