{-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE TypeFamilies #-} ----------------------------------------------------------------------------- -- | -- Module : Diagrams.ThreeD.Projection -- Copyright : (c) 2014 diagrams team (see LICENSE) -- License : BSD-style (see LICENSE) -- Maintainer : diagrams-discuss@googlegroups.com -- -- 3D projections are a way of viewing a three-dimensional objects on a -- two-dimensional plane. -- -- This module can be used with the functions in "Linear.Projection". -- -- Disclaimer: This module should be considered experimental and is -- likely to change. -- ----------------------------------------------------------------------------- module Diagrams.ThreeD.Projection ( -- * Orthographic projections -- $orthographic -- ** Parallel projections facingXY , facingXZ , facingYZ -- ** axonometric -- $axonometric -- *** Isometric projections -- $isometric , isometricApply , isometric , lookingAt -- ** Affine maps , m44AffineApply , m44AffineMap , m33AffineApply , m33AffineMap -- * Perspective projections -- $perspective -- ** Perspective deformations , m44Deformation , module Linear.Projection ) where import Control.Lens hiding (transform) import Data.Functor.Rep import Diagrams.Core import Diagrams.Deform import Diagrams.Direction import Diagrams.LinearMap import Diagrams.ThreeD.Types (P3) import Diagrams.ThreeD.Vector import Linear as L import Linear.Affine import Linear.Projection ------------------------------------------------------------------------ -- Orthographic projections ------------------------------------------------------------------------ -- $orthographic -- Orthographic projections are a form of parallel projections where are -- projection lines are orthogonal to the projection plane. -- Parallel projections -- | Look at the xy-plane with y as the up direction. facingXY :: (Epsilon n, Floating n) => AffineMap V3 V2 n facingXY = lookingAt unitZ origin yDir -- | Look at the xz-plane with z as the up direction. facingXZ :: (Epsilon n, Floating n) => AffineMap V3 V2 n facingXZ = lookingAt unitY origin zDir -- | Look at the yz-plane with z as the up direction. facingYZ :: (Epsilon n, Floating n) => AffineMap V3 V2 n facingYZ = lookingAt unitX origin zDir -- $axonometric -- Axonometric projections are a type of orthographic projection where -- the object is rotated along one or more of its axes relative to the -- plane of projection. -- $isometric -- Isometric projections are when the scale along each axis of the -- projection is the same and the angle between any axis is 120 -- degrees. -- | Apply an isometric projection given the up direction isometricApply :: (InSpace V3 n a, InSpace V2 n b, AffineMappable a b, Floating n, Epsilon n) => Direction V3 n -> a -> b isometricApply up = amap (isometric up) -- | Make an isometric affine map with the given up direction. isometric :: (Floating n, Epsilon n) => Direction V3 n -> AffineMap V3 V2 n isometric up = m44AffineMap m where m = lookAt (V3 1 1 1) zero (fromDirection up) lookingAt :: (Epsilon n, Floating n) => P3 n -- ^ Eye -> P3 n -- ^ Center -> Direction V3 n -- ^ Up -> AffineMap V3 V2 n lookingAt (P cam) (P center) d = m44AffineMap m where m = lookAt cam center (d^._Dir) -- | Apply the affine part of a homogeneous matrix. m44AffineApply :: (InSpace V3 n a, InSpace V2 n b, AffineMappable a b) => M44 n -> a -> b m44AffineApply = amap . m44AffineMap -- | Create an 'AffineMap' from a 4x4 homogeneous matrix, ignoring any -- perspective transforms. m44AffineMap :: Num n => M44 n -> AffineMap V3 V2 n m44AffineMap m = AffineMap (LinearMap f) (f v) where f = view _xy . (m' !*) m' = m ^. linearTransform v = m ^. L.translation -- | Apply a transformation matrix and translation. m33AffineApply :: (InSpace V3 n a, InSpace V2 n b, AffineMappable a b) => M33 n -> V2 n -> a -> b m33AffineApply m = amap . m33AffineMap m -- | Create an 'AffineMap' from a 3x3 transformation matrix and a -- translation vector. m33AffineMap :: Num n => M33 n -> V2 n -> AffineMap V3 V2 n m33AffineMap m = AffineMap (LinearMap f) where f = view _xy . (m !*) -- | Extract the linear transform part of a homogeneous matrix. linearTransform :: (Representable u, R3 v, R3 u) => Lens' (u (v n)) (M33 n) linearTransform = column _xyz . _xyz ------------------------------------------------------------------------ -- Perspective transforms ------------------------------------------------------------------------ -- For the time being projective transforms use the deformable class. -- Eventually we would like to replace this with a more specialised -- method. -- $perspective -- Perspective projections are when closer objects appear bigger. -- | Make a deformation from a 4x4 homogeneous matrix. m44Deformation :: Fractional n => M44 n -> Deformation V3 V2 n m44Deformation m = Deformation (P . view _xy . normalizePoint . (m !*) . point . view _Point)