wumpus-core-0.34.0: src/Wumpus/Core/AffineTrans.hs
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleContexts #-}
{-# OPTIONS -Wall #-}
------------------------------------------------------------------------------
-- |
-- Module : Wumpus.Core.AffineTrans
-- Copyright : (c) Stephen Tetley 2009-2010
-- License : BSD3
--
-- Maintainer : stephen.tetley@gmail.com
-- Stability : unstable
-- Portability : GHC
--
-- Affine transformations.
--
-- The common affine transformations represented as type classes -
-- scaling, rotation, translation.
--
-- Internally, when a picture is composed and transformed, Wumpus
-- only transforms the bounding box - transformations of the
-- picture content (paths or text labels) are communicated to
-- PostScript or SVG for final rendering. This is because Wumpus
-- has no access to the paths that make fonts so cannot transform
-- them directly.
--
-- As well as Pictures, some elements - e.g. Vectors, Points and
-- BoundingBoxes - are also instances of the affine classes. The
-- implementation of the instances considers that under
-- transformation these objects are implicitly within the standard
-- affine frame (origin at point zero and unit basis vectors for
-- the horizontal and vertical).
--
-- This assumption cannot hold for primitives because text is
-- special in PostScript and SVG, so there are no instances of
-- the affine classes for Primitive or PrimElement.
--
-- To generate efficient PostScript, Wumpus relies on the matrix
-- representations of the affine transformations being invertible.
-- Do not scale elements by zero!
--
--------------------------------------------------------------------------------
module Wumpus.Core.AffineTrans
(
-- * Type classes
Transform(..)
, Rotate(..)
, RotateAbout(..)
, Scale(..)
, Translate(..)
-- * Common rotations
, rotate30
, rotate30About
, rotate45
, rotate45About
, rotate60
, rotate60About
, rotate90
, rotate90About
, rotate120
, rotate120About
-- * Common scalings
, uniformScale
, reflectX
, reflectY
-- * Translate by a vector
, translateBy
-- * Reflections in supplied plane rather than about the origin
, reflectXPlane
, reflectYPlane
) where
import Wumpus.Core.Geometry
--------------------------------------------------------------------------------
-- Affine transformations
-- | Apply a matrix transformation directly.
--
class Transform t where
transform :: u ~ DUnit t => Matrix3'3 u -> t -> t
-- | Type class for rotation.
--
class Rotate t where
rotate :: Radian -> t -> t
instance Num u => Transform (Point2 u) where
transform ctm = (ctm *#)
instance Num u => Transform (Vec2 u) where
transform ctm = (ctm *#)
instance (Floating u, Real u) => Rotate (Point2 u) where
rotate a = ((rotationMatrix a) *#)
instance (Floating u, Real u) => Rotate (Vec2 u) where
rotate a = ((rotationMatrix a) *#)
-- | Type class for rotation about a point.
--
class RotateAbout t where
rotateAbout :: u ~ DUnit t => Radian -> Point2 u -> t -> t
instance (Floating u, Real u) => RotateAbout (Point2 u) where
rotateAbout a pt = ((originatedRotationMatrix a pt) *#)
instance (Floating u, Real u) => RotateAbout (Vec2 u) where
rotateAbout a pt = ((originatedRotationMatrix a pt) *#)
--------------------------------------------------------------------------------
-- Scale
-- | Type class for scaling.
--
class Scale t where
scale :: u ~ DUnit t => u -> u -> t -> t
instance Num u => Scale (Point2 u) where
scale sx sy = ((scalingMatrix sx sy) *#)
instance Num u => Scale (Vec2 u) where
scale sx sy = ((scalingMatrix sx sy) *#)
--------------------------------------------------------------------------------
-- Translate
-- | Type class for translation.
--
class Translate t where
translate :: DUnit t -> DUnit t -> t -> t
instance Num u => Translate (Point2 u) where
translate dx dy (P2 x y) = P2 (x+dx) (y+dy)
instance Num u => Translate (Vec2 u) where
translate dx dy (V2 x y) = V2 (x+dx) (y+dy)
--------------------------------------------------------------------------------
-- Common rotations
-- | Rotate by 30 degrees about the origin.
--
rotate30 :: Rotate t => t -> t
rotate30 = rotate (pi/6)
-- | Rotate by 30 degrees about the supplied point.
--
rotate30About :: (RotateAbout t, DUnit t ~ u) => Point2 u -> t -> t
rotate30About = rotateAbout (pi/6)
-- | Rotate by 45 degrees about the origin.
--
rotate45 :: Rotate t => t -> t
rotate45 = rotate (pi/4)
-- | Rotate by 45 degrees about the supplied point.
--
rotate45About :: (RotateAbout t, DUnit t ~ u) => Point2 u -> t -> t
rotate45About = rotateAbout (pi/4)
-- | Rotate by 60 degrees about the origin.
--
rotate60 :: Rotate t => t -> t
rotate60 = rotate (2*pi/3)
-- | Rotate by 60 degrees about the supplied point.
--
rotate60About :: (RotateAbout t, DUnit t ~ u) => Point2 u -> t -> t
rotate60About = rotateAbout (2*pi/3)
-- | Rotate by 90 degrees about the origin.
--
rotate90 :: Rotate t => t -> t
rotate90 = rotate (pi/2)
-- | Rotate by 90 degrees about the supplied point.
--
rotate90About :: (RotateAbout t, DUnit t ~ u) => Point2 u -> t -> t
rotate90About = rotateAbout (pi/2)
-- | Rotate by 120 degrees about the origin.
--
rotate120 :: Rotate t => t -> t
rotate120 = rotate (4*pi/3)
-- | Rotate by 120 degrees about the supplied point.
--
rotate120About :: (RotateAbout t, DUnit t ~ u) => Point2 u -> t -> t
rotate120About = rotateAbout (4*pi/3)
--------------------------------------------------------------------------------
-- Common scalings
-- | Scale both x and y dimensions by the same amount.
--
uniformScale :: (Scale t, DUnit t ~ u) => u -> t -> t
uniformScale a = scale a a
-- | Reflect in the X-plane about the origin.
--
reflectX :: (Num u, Scale t, DUnit t ~ u) => t -> t
reflectX = scale (-1) 1
-- | Reflect in the Y-plane about the origin.
--
reflectY :: (Num u, Scale t, DUnit t ~ u) => t -> t
reflectY = scale 1 (-1)
--------------------------------------------------------------------------------
-- Translations
-- | Translate by the x and y components of a vector.
--
translateBy :: (Translate t, DUnit t ~ u) => Vec2 u -> t -> t
translateBy (V2 x y) = translate x y
--------------------------------------------------------------------------------
-- Translation and scaling
-- | Reflect in the X plane that intersects the supplied point.
--
reflectXPlane :: (Num u, Scale t, Translate t, u ~ DUnit t)
=> Point2 u -> t -> t
reflectXPlane (P2 x y) = translate x y . scale (-1) 1 . translate (-x) (-y)
-- | Reflect in the Y plane that intersects the supplied point.
--
reflectYPlane :: (Num u, Scale t, Translate t, u ~ DUnit t)
=> Point2 u -> t -> t
reflectYPlane (P2 x y) = translate x y . scale 1 (-1) . translate (-x) (-y)