{-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeOperators #-} ----------------------------------------------------------------------------- -- | -- Module : Diagrams.Coordinates -- Copyright : (c) 2012 diagrams-lib team (see LICENSE) -- License : BSD-style (see LICENSE) -- Maintainer : diagrams-discuss@googlegroups.com -- -- Nice syntax for constructing and pattern-matching on literal -- points and vectors. -- ----------------------------------------------------------------------------- module Diagrams.Coordinates ( (:&)(..), Coordinates(..) -- * Lenses for particular axes , HasX(..), HasY(..), HasZ(..) ) where import Control.Lens (Lens') import Diagrams.Core.Points -- | A pair of values, with a convenient infix (left-associative) -- data constructor. data a :& b = a :& b deriving (Eq, Ord, Show) infixl 7 :& -- | Types which are instances of the @Coordinates@ class can be -- constructed using '^&' (for example, a three-dimensional vector -- could be constructed by @1 ^& 6 ^& 3@), and deconstructed using -- 'coords'. A common pattern is to use 'coords' in conjunction -- with the @ViewPatterns@ extension, like so: -- -- @ -- foo :: Vector3 -> ... -- foo (coords -> x :& y :& z) = ... -- @ class Coordinates c where -- | The type of the final coordinate. type FinalCoord c :: * -- | The type of everything other than the final coordinate. type PrevDim c :: * -- | Decomposition of @c@ into applications of ':&'. type Decomposition c :: * -- Decomposition c = Decomposition (PrevDim c) :& FinalCoord c (essentially) -- | Construct a value of type @c@ by providing something of one -- less dimension (which is perhaps itself recursively constructed -- using @(^&)@) and a final coordinate. For example, -- -- @ -- 2 ^& 3 :: P2 -- 3 ^& 5 ^& 6 :: R3 -- @ -- -- Note that @^&@ is left-associative. (^&) :: PrevDim c -> FinalCoord c -> c -- | Prefix synonym for @^&@. pr stands for pair of @PrevDim@, @FinalCoord@ pr :: PrevDim c -> FinalCoord c -> c pr = (^&) -- | Decompose a value of type @c@ into its constituent coordinates, -- stored in a nested @(:&)@ structure. coords :: c -> Decomposition c infixl 7 ^& -- Some standard instances for plain old tuples instance Coordinates (a,b) where type FinalCoord (a,b) = b type PrevDim (a,b) = a type Decomposition (a,b) = a :& b x ^& y = (x,y) coords (x,y) = x :& y instance Coordinates (a,b,c) where type FinalCoord (a,b,c) = c type PrevDim (a,b,c) = (a,b) type Decomposition (a,b,c) = Decomposition (a,b) :& c (x,y) ^& z = (x,y,z) coords (x,y,z) = coords (x,y) :& z instance Coordinates (a,b,c,d) where type FinalCoord (a,b,c,d) = d type PrevDim (a,b,c,d) = (a,b,c) type Decomposition (a,b,c,d) = Decomposition (a,b,c) :& d (w,x,y) ^& z = (w,x,y,z) coords (w,x,y,z) = coords (w,x,y) :& z instance Coordinates v => Coordinates (Point v) where type FinalCoord (Point v) = FinalCoord v type PrevDim (Point v) = PrevDim v type Decomposition (Point v) = Decomposition v x ^& y = P (x ^& y) coords (P v) = coords v -- | The class of types with at least one coordinate, called _x. class HasX t where _x :: Lens' t Double -- | The class of types with at least two coordinates, the second called _y. class HasY t where _y :: Lens' t Double -- | The class of types with at least three coordinates, the third called _z. class HasZ t where _z :: Lens' t Double