penrose-0.1.0.2: src/Shapes.hs
--------------------------------------------------------------------------------
-- Geometry module in Penrose
-- Currently supporting:
-- Circle
-- Square
-- Point
-- Arrow
-- Label
--------------------------------------------------------------------------------
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DuplicateRecordFields #-}
module Shapes where
-- module Shapes (Obj, Obj') where
import Data.Aeson
import Data.Monoid ((<>))
import GHC.Generics
import Graphics.Gloss
type Name = String
class Located a where
getX :: a -> Float
getY :: a -> Float
setX :: Float -> a -> a
setY :: Float -> a -> a
class Selectable a where
select :: a -> a
deselect :: a -> a
selected :: a -> Bool
class Sized a where
getSize :: a -> Float
setSize :: Float -> a -> a
class Named a where
getName :: a -> Name
setName :: Name -> a -> a
-- data BBox = BBox {
-- cx :: Float,
-- cy :: Float,
-- h :: Float,
-- w :: Float
-- } deriving (Show, Eq, Generic)
-- instance ToJSON BBox
-- instance FromJSON BBox
-------
data SolidArrow = SolidArrow { startx :: Float
, starty :: Float
, endx :: Float
, endy :: Float
, thickness :: Float -- the maximum thickness, i.e. the thickness of the head
, selsa :: Bool -- is the circle currently selected? (mouse is dragging it)
, namesa :: String
, colorsa :: Color
-- , bbox :: BBox
}
deriving (Eq, Show, Generic)
instance Located SolidArrow where
-- getX a = endx a - startx a
-- getY a = endy a - starty a
getX a = startx a
getY a = starty a
setX x c = c { startx = x } -- TODO
setY y c = c { starty = y }
instance Selectable SolidArrow where
select x = x { selsa = True }
deselect x = x { selsa = False }
selected x = selsa x
instance Named SolidArrow where
getName a = namesa a
setName x a = a { namesa = x }
instance ToJSON SolidArrow
instance FromJSON SolidArrow
-------
data Circ = Circ { xc :: Float
, yc :: Float
, r :: Float
, selc :: Bool -- is the circle currently selected? (mouse is dragging it)
, namec :: String
, colorc :: Color }
deriving (Eq, Show, Generic)
instance Located Circ where
getX c = xc c
getY c = yc c
setX x c = c { xc = x }
setY y c = c { yc = y }
instance Selectable Circ where
select x = x { selc = True }
deselect x = x { selc = False }
selected x = selc x
instance Sized Circ where
getSize x = r x
setSize size x = x { r = size }
instance Named Circ where
getName c = namec c
setName x c = c { namec = x }
instance ToJSON Circ
instance FromJSON Circ
----------------------
data Square = Square { xs :: Float
, ys :: Float
, side :: Float
, ang :: Float -- angle for which the obj is rotated
, sels :: Bool -- is the circle currently selected? (mouse is dragging it)
, names :: String
, colors :: Color }
deriving (Eq, Show, Generic)
instance Located Square where
getX s = xs s
getY s = ys s
setX x s = s { xs = x }
setY y s = s { ys = y }
instance Selectable Square where
select x = x { sels = True }
deselect x = x { sels = False }
selected x = sels x
instance Sized Square where
getSize x = side x
setSize size x = x { side = size }
instance Named Square where
getName s = names s
setName x s = s { names = x }
instance ToJSON Square
instance FromJSON Square
-------
data Label = Label { xl :: Float
, yl :: Float
, wl :: Float
, hl :: Float
, textl :: String
-- , scalel :: Float -- calculate h,w from it
, sell :: Bool -- selected label
, namel :: String }
deriving (Eq, Show, Generic)
instance Located Label where
getX l = xl l
getY l = yl l
setX x l = l { xl = x }
setY y l = l { yl = y }
instance Selectable Label where
select x = x { sell = True }
deselect x = x { sell = False }
selected x = sell x
instance Sized Label where
getSize x = xl x -- TODO generalize label size, distance to corner? ignores scale
setSize size x = x { xl = size, yl = size } -- TODO currently sets both of them, ignores scale
-- changing a label's size doesn't actually do anything right now, but should use the scale
-- and the base font size
instance Named Label where
getName l = namel l
setName x l = l { namel = x }
instance ToJSON Label
instance FromJSON Label
------
data Pt = Pt { xp :: Float
, yp :: Float
, selp :: Bool
, namep :: String }
deriving (Eq, Show, Generic)
instance Located Pt where
getX p = xp p
getY p = yp p
setX x p = p { xp = x }
setY y p = p { yp = y }
instance Selectable Pt where
select x = x { selp = True }
deselect x = x { selp = False }
selected x = selp x
instance Named Pt where
getName p = namep p
setName x p = p { namep = x }
instance ToJSON Pt
instance FromJSON Pt
data Obj = S Square
| C Circ
| L Label
| P Pt
| A SolidArrow
deriving (Eq, Show, Generic)
instance ToJSON Obj
instance FromJSON Obj
instance FromJSON Color where
parseJSON = withObject "Color" $ \v -> makeColor
<$> v .: "r"
<*> v .: "g"
<*> v .: "b"
<*> v .: "a"
instance ToJSON Color where
-- this generates a Value
toJSON c =
let (r, g, b, a) = rgbaOfColor c in
object ["r" .= r, "g" .= g, "b" .= b, "a" .= a]
toEncoding c =
let (r, g, b, a) = rgbaOfColor c in
pairs ("r" .= r <> "g" .= g <> "b" .= b <> "a" .= a)
-- TODO: is there some way to reduce the top-level boilerplate?
instance Located Obj where
getX o = case o of
C c -> getX c
L l -> getX l
P p -> getX p
S s -> getX s
A a -> getX a
getY o = case o of
C c -> getY c
L l -> getY l
P p -> getY p
S s -> getY s
A a -> getY a
setX x o = case o of
C c -> C $ setX x c
L l -> L $ setX x l
P p -> P $ setX x p
S s -> S $ setX x s
A a -> A $ setX x a
setY y o = case o of
C c -> C $ setY y c
L l -> L $ setY y l
P p -> P $ setY y p
S s -> S $ setY y s
A a -> A $ setY y a
instance Selectable Obj where
select x = case x of
C c -> C $ select c
L l -> L $ select l
P p -> P $ select p
S s -> S $ select s
A a -> A $ select a
deselect x = case x of
C c -> C $ deselect c
L l -> L $ deselect l
P p -> P $ deselect p
S s -> S $ deselect s
A a -> A $ deselect a
selected x = case x of
C c -> selected c
L l -> selected l
P p -> selected p
S s -> selected s
A a -> selected a
instance Sized Obj where
getSize o = case o of
C c -> getSize c
S s -> getSize s
L l -> getSize l
setSize x o = case o of
C c -> C $ setSize x c
L l -> L $ setSize x l
S s -> S $ setSize x s
instance Named Obj where
getName o = case o of
C c -> getName c
L l -> getName l
P p -> getName p
S s -> getName s
A a -> getName a
setName x o = case o of
C c -> C $ setName x c
L l -> L $ setName x l
P p -> P $ setName x p
S s -> S $ setName x s
A a -> A $ setName x a
data SolidArrow' a = SolidArrow' { startx' :: a
, starty' :: a
, endx' :: a
, endy' :: a
, thickness' :: a -- the maximum thickness, i.e. the thickness of the head
, selsa' :: Bool -- is the circle currently selected? (mouse is dragging it)
, namesa' :: String
, colorsa' :: Color }
deriving (Eq, Show)
data Circ' a = Circ' { xc' :: a
, yc' :: a
, r' :: a
, selc' :: Bool -- is the circle currently selected? (mouse is dragging it)
, namec' :: String
, colorc' :: Color }
deriving (Eq, Show)
data Label' a = Label' { xl' :: a
, yl' :: a
, wl' :: a
, hl' :: a
, textl' :: String
, sell' :: Bool -- selected label
, namel' :: String }
deriving (Eq, Show)
data Pt' a = Pt' { xp' :: a
, yp' :: a
, selp' :: Bool
, namep' :: String }
deriving (Eq, Show)
data Square' a = Square' { xs' :: a
, ys' :: a
, side' :: a
, ang' :: Float -- angle for which the obj is rotated
, sels' :: Bool
, names' :: String
, colors' :: Color }
deriving (Eq, Show)
instance Named (SolidArrow' a) where
getName sa = namesa' sa
setName x sa = sa { namesa' = x }
instance Named (Circ' a) where
getName c = namec' c
setName x c = c { namec' = x }
instance Named (Square' a) where
getName s = names' s
setName x s = s { names' = x }
instance Named (Label' a) where
getName l = namel' l
setName x l = l { namel' = x }
instance Named (Pt' a) where
getName p = namep' p
setName x p = p { namep' = x }
instance Named (Obj' a) where
getName o = case o of
C' c -> getName c
L' l -> getName l
P' p -> getName p
S' s -> getName s
A' a -> getName a
setName x o = case o of
C' c -> C' $ setName x c
S' s -> S' $ setName x s
L' l -> L' $ setName x l
P' p -> P' $ setName x p
A' a -> A' $ setName x a
-- instance Located (Obj' a) where
-- getX o = case o of
-- C' c -> xc' c
-- L' l -> xl' l
-- P' p -> xp' p
-- S' s -> xs' s
-- A' a -> startx' a
-- getY o = case o of
-- C' c -> yc' c
-- L' l -> yl' l
-- P' p -> yp' p
-- S' s -> ys' s
-- A' a -> starty' a
-- setX x o = case o of
-- C' c -> C $ setX' x c
-- L' l -> L $ setX' x l
-- P' p -> P $ setX' x p
-- S' s -> S $ setX' x s
-- A' a -> A $ setX' x a
-- setY y o = case o of
-- C' c -> C' $ setY' y c
-- L' l -> L' $ setY' y l
-- P' p -> P' $ setY' y p
-- S' s -> S' $ setY' y s
-- A' a -> A' $ setY' y a
data Obj' a = C' (Circ' a) | L' (Label' a) | P' (Pt' a) | S' (Square' a)
| A' (SolidArrow' a) deriving (Eq, Show)