Rasterific-0.2: src/Graphics/Rasterific/Transformations.hs
-- | This module provide some helpers in order
-- to perform basic geometric transformation on
-- the drawable primitives.
--
-- You can combine the transformation is `mappend` or
-- the `(\<\>)` operator from "Data.Monoid" .
module Graphics.Rasterific.Transformations
( Transformation( .. )
, applyTransformation
, translate
, scale
, rotate
, rotateCenter
, inverseTransformation
) where
import Data.Monoid( Monoid( .. ), (<>) )
import Graphics.Rasterific.Types
import Linear( V2( .. ) )
-- | Represent a 3*3 matrix for homogenous coordinates.
--
-- > | A C E |
-- > | B D F |
-- > | 0 0 1 |
--
data Transformation = Transformation
{ _transformA :: {-# UNPACK #-} !Float
, _transformC :: {-# UNPACK #-} !Float
, _transformE :: {-# UNPACK #-} !Float -- ^ X translation
, _transformB :: {-# UNPACK #-} !Float
, _transformD :: {-# UNPACK #-} !Float
, _transformF :: {-# UNPACK #-} !Float -- ^ Y translation
}
deriving (Eq, Show)
transformCombine :: Transformation -> Transformation -> Transformation
transformCombine (Transformation a c e
b d f)
(Transformation a' c' e'
b' d' f') =
Transformation (a * a' + c * b' {- below b' is zero -})
(a * c' + c * d' {- below d' is zero -})
(a * e' + c * f' + e {- below f' is one -})
(b * a' + d * b' {- below b' is zero -})
(b * c' + d * d' {- below d' is zero -})
(b * e' + d * f' + f {- below f' is one -})
instance Monoid Transformation where
mappend = transformCombine
mempty = Transformation 1 0 0
0 1 0
-- | Effectively transform a point given a transformation.
applyTransformation :: Transformation -> Point -> Point
applyTransformation (Transformation a c e
b d f) (V2 x y) =
V2 (a * x + y * c + e) (b * x + d * y + f)
-- | Create a transformation representing a rotation
-- on the plane.
--
-- > fill . transform (applyTransformation $ rotate 0.2)
-- > $ rectangle (V2 40 40) 120 120
--
-- <<docimages/transform_rotate.png>>
--
rotate :: Float -- ^ Rotation angle in radian.
-> Transformation
rotate angle = Transformation ca (-sa) 0
sa ca 0
where ca = cos angle
sa = sin angle
-- | Create a transformation representing a rotation
-- on the plane. The rotation center is given in parameter
--
-- > fill . transform (applyTransformation $ rotateCenter 0.2 (V2 200 200))
-- > $ rectangle (V2 40 40) 120 120
--
-- <<docimages/transform_rotate_center.png>>
--
rotateCenter :: Float -- ^ Rotation angle in radian
-> Point -- ^ Rotation center
-> Transformation
rotateCenter angle p =
translate p <> rotate angle <> translate (negate p)
-- | Perform a scaling of the given primitives.
--
-- > fill . transform (applyTransformation $ scale 2 2)
-- > $ rectangle (V2 40 40) 40 40
--
-- <<docimages/transform_scale.png>>
--
scale :: Float -> Float -> Transformation
scale scaleX scaleY =
Transformation scaleX 0 0
0 scaleY 0
-- | Perform a translation of the given primitives.
--
-- > fill . transform (applyTransformation $ translate (V2 100 100))
-- > $ rectangle (V2 40 40) 40 40
--
-- <<docimages/transform_translate.png>>
--
translate :: Vector -> Transformation
translate (V2 x y) =
Transformation 1 0 x
0 1 y
-- | Inverse a transformation (if possible)
inverseTransformation :: Transformation -> Transformation
inverseTransformation (Transformation a c e
b d f) =
Transformation a' c' e' b' d' f'
where det = a * d - b * c
a' = d / det
c' = (- c) / det
e' = (c * f - e * d) / det
b' = (- b) / det
d' = a / det
f' = (e * b - a * f) / det