linear-accelerate-0.6.0.0: src/Data/Array/Accelerate/Linear/Plucker.hs
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE RebindableSyntax #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE ViewPatterns #-}
{-# OPTIONS_GHC -fno-warn-name-shadowing #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
-----------------------------------------------------------------------------
-- |
-- Module : Data.Array.Accelerate.Linear.Plucker
-- Copyright : 2014 Edward Kmett, Charles Durham,
-- [2015..2018] Trevor L. McDonell
-- License : BSD-style (see the file LICENSE)
--
-- Maintainer : Edward Kmett <ekmett@gmail.com>
-- Stability : experimental
-- Portability : non-portable
--
-- Plücker coordinates for lines in 3d homogeneous space.
----------------------------------------------------------------------------
module Data.Array.Accelerate.Linear.Plucker (
Plucker(..),
squaredError,
isotropic,
(><),
plucker,
plucker3D,
-- * operations on lines
LinePass(..),
parallel,
intersects,
passes,
quadranceToOrigin,
closestToOrigin,
isLine,
-- * Basis elements
p01, p02, p03,
p10, p12, p13,
p20, p21, p23,
p30, p31, p32,
) where
import Data.Array.Accelerate hiding ( fromInteger )
import Data.Array.Accelerate.Data.Functor
import Data.Array.Accelerate.Smart
import Data.Array.Accelerate.Product
import Data.Array.Accelerate.Array.Sugar
import Data.Array.Accelerate.Linear.Epsilon
import Data.Array.Accelerate.Linear.Lift
import Data.Array.Accelerate.Linear.Metric
import Data.Array.Accelerate.Linear.V2
import Data.Array.Accelerate.Linear.V3
import Data.Array.Accelerate.Linear.V4
import Data.Array.Accelerate.Linear.Vector
import Control.Lens
import Data.Function
import Data.Typeable
import Linear.Plucker ( Plucker(..), LinePass(..) )
import Prelude ( fromInteger )
import qualified Linear.Plucker as L
import qualified Prelude as P
-- | Valid Plücker coordinates @p@ will have @'squaredError' p '==' 0@
--
-- That said, floating point makes a mockery of this claim, so you may want to
-- use 'nearZero'.
--
squaredError :: forall a. Num a => Exp (Plucker a) -> Exp a
squaredError = lift1 (L.squaredError :: Plucker (Exp a) -> Exp a)
-- | This isn't the actual metric because this bilinear form gives rise to an
-- isotropic quadratic space.
--
infixl 5 ><
(><) :: forall a. Num a => Exp (Plucker a) -> Exp (Plucker a) -> Exp a
(><) = lift2 ((L.><) :: Plucker (Exp a) -> Plucker (Exp a) -> Exp a)
-- | Checks if the line is near-isotropic (isotropic vectors in this quadratic
-- space represent lines in real 3D space).
--
isotropic :: Epsilon a => Exp (Plucker a) -> Exp Bool
isotropic a = nearZero (a >< a)
-- | Given a pair of points represented by homogeneous coordinates generate
-- Plücker coordinates for the line through them, directed from the second
-- towards the first.
--
plucker :: forall a. Num a => Exp (V4 a) -> Exp (V4 a) -> Exp (Plucker a)
plucker = lift2 (L.plucker :: V4 (Exp a) -> V4 (Exp a) -> Plucker (Exp a))
-- | Given a pair of 3D points, generate Plücker coordinates for the line
-- through them, directed from the second towards the first.
--
plucker3D :: forall a. Num a => Exp (V3 a) -> Exp (V3 a) -> Exp (Plucker a)
plucker3D = lift2 (L.plucker3D :: V3 (Exp a) -> V3 (Exp a) -> Plucker (Exp a))
-- | Checks if two lines intersect (or nearly intersect).
--
intersects :: (Epsilon a, Ord a) => Exp (Plucker a) -> Exp (Plucker a) -> Exp Bool
intersects a b = not (a `parallel` b) && passes a b == constant Coplanar
-- | Check how two lines pass each other. @passes l1 l2@ describes @l2@ when
-- looking down @l1@.
--
passes :: (Epsilon a, Ord a) => Exp (Plucker a) -> Exp (Plucker a) -> Exp LinePass
passes a b =
if nearZero s then constant Coplanar else
if s > 0 then constant Counterclockwise
else constant Clockwise
where
s = (lift u1 `dot` lift v2) + (lift u2 `dot` lift v1)
V2 u1 v1 = toUV a
V2 u2 v2 = toUV b
-- | Checks if two lines are parallel.
--
parallel :: Epsilon a => Exp (Plucker a) -> Exp (Plucker a) -> Exp Bool
parallel a b = nearZero $ lift u1 `cross` lift u2
where
V2 u1 _ = toUV a
V2 u2 _ = toUV b
-- | Represent a Plücker coordinate as a pair of 3-tuples, typically denoted
-- U and V.
--
toUV :: Elt a => Exp (Plucker a) -> V2 (V3 (Exp a))
toUV (unlift -> Plucker a b c d e f) = V2 (V3 a b c) (V3 d e f)
-- | The minimum squared distance of a line from the origin.
--
quadranceToOrigin :: Fractional a => Exp (Plucker a) -> Exp a
quadranceToOrigin p = (lift v `dot` lift v) / (lift u `dot` lift u)
where
V2 u v = toUV p
-- | The point where a line is closest to the origin.
--
closestToOrigin :: Fractional a => Exp (Plucker a) -> Exp (V3 a)
closestToOrigin p = normalizePoint . lift $ V4 x y z (lift u `dot` lift u)
where
V2 u v = toUV p
V3 x y z = unlift $ lift v `cross` lift u
-- | Not all 6-dimensional points correspond to a line in 3D. This predicate
-- tests that a Plücker coordinate lies on the Grassmann manifold, and does
-- indeed represent a 3D line.
--
isLine :: Epsilon a => Exp (Plucker a) -> Exp Bool
isLine p = nearZero $ lift u `dot` lift v
where
V2 u v = toUV p
-- | These elements form a basis for the Plücker space, or the Grassmanian
-- manifold @Gr(2,V4)@.
--
-- @
-- 'p01' :: 'Lens'' ('Plucker' a) a
-- 'p02' :: 'Lens'' ('Plucker' a) a
-- 'p03' :: 'Lens'' ('Plucker' a) a
-- 'p23' :: 'Lens'' ('Plucker' a) a
-- 'p31' :: 'Lens'' ('Plucker' a) a
-- 'p12' :: 'Lens'' ('Plucker' a) a
-- @
--
p01, p02, p03, p23, p31, p12 :: Elt a => Lens' (Exp (Plucker a)) (Exp a)
p01 = liftLens (L.p01 :: Lens' (Plucker (Exp a)) (Exp a))
p02 = liftLens (L.p02 :: Lens' (Plucker (Exp a)) (Exp a))
p03 = liftLens (L.p03 :: Lens' (Plucker (Exp a)) (Exp a))
p23 = liftLens (L.p23 :: Lens' (Plucker (Exp a)) (Exp a))
p31 = liftLens (L.p31 :: Lens' (Plucker (Exp a)) (Exp a))
p12 = liftLens (L.p12 :: Lens' (Plucker (Exp a)) (Exp a))
-- | These elements form an alternate basis for the Plücker space, or the
-- Grassmanian manifold @Gr(2,V4)@.
--
-- @
-- 'p10' :: 'Num' a => 'Lens'' ('Plucker' a) a
-- 'p20' :: 'Num' a => 'Lens'' ('Plucker' a) a
-- 'p30' :: 'Num' a => 'Lens'' ('Plucker' a) a
-- 'p32' :: 'Num' a => 'Lens'' ('Plucker' a) a
-- 'p13' :: 'Num' a => 'Lens'' ('Plucker' a) a
-- 'p21' :: 'Num' a => 'Lens'' ('Plucker' a) a
-- @
--
p10, p20, p30, p32, p13, p21 :: Num a => Lens' (Exp (Plucker a)) (Exp a)
p10 = anti p01
p20 = anti p02
p30 = anti p03
p32 = anti p23
p13 = anti p31
p21 = anti p21
anti :: (P.Functor f, Num a) => ((Exp a -> f (Exp a)) -> r) -> (Exp a -> f (Exp a)) -> r
anti k f = k (P.fmap negate . f . negate)
-- Instances
-- ---------
deriving instance Typeable Plucker
instance Metric Plucker
instance Additive Plucker
type instance EltRepr (Plucker a) = EltRepr (a, a, a, a, a, a)
instance Elt a => Elt (Plucker a) where
eltType _ = eltType (undefined :: (a,a,a,a,a,a))
toElt p = case toElt p of
(x, y, z, w, u, v) -> Plucker x y z w u v
fromElt (Plucker x y z w u v) = fromElt (x, y, z, w, u, v)
instance cst a => IsProduct cst (Plucker a) where
type ProdRepr (Plucker a) = ProdRepr (a,a,a,a,a,a)
fromProd p (Plucker x y z w u v) = fromProd p (x, y, z, w, u, v)
toProd p t = case toProd p t of
(x, y, z, w, u, v) -> Plucker x y z w u v
prod p _ = prod p (undefined :: (a,a,a,a,a,a))
instance (Lift Exp a, Elt (Plain a)) => Lift Exp (Plucker a) where
type Plain (Plucker a) = Plucker (Plain a)
-- lift = Exp . Tuple . F.foldl SnocTup NilTup
lift (Plucker x y z w u v) =
Exp $ Tuple $
NilTup `SnocTup` lift x
`SnocTup` lift y
`SnocTup` lift z
`SnocTup` lift w
`SnocTup` lift u
`SnocTup` lift v
instance Elt a => Unlift Exp (Plucker (Exp a)) where
unlift t = Plucker
(Exp $ SuccTupIdx (SuccTupIdx (SuccTupIdx (SuccTupIdx (SuccTupIdx ZeroTupIdx)))) `Prj` t)
(Exp $ SuccTupIdx (SuccTupIdx (SuccTupIdx (SuccTupIdx ZeroTupIdx))) `Prj` t)
(Exp $ SuccTupIdx (SuccTupIdx (SuccTupIdx ZeroTupIdx)) `Prj` t)
(Exp $ SuccTupIdx (SuccTupIdx ZeroTupIdx) `Prj` t)
(Exp $ SuccTupIdx ZeroTupIdx `Prj` t)
(Exp $ ZeroTupIdx `Prj` t)
instance (Elt a, Elt b) => Each (Exp (Plucker a)) (Exp (Plucker b)) (Exp a) (Exp b) where
each = liftLens (each :: Traversal (Plucker (Exp a)) (Plucker (Exp b)) (Exp a) (Exp b))
instance Eq a => Eq (Plucker a) where
(==) = (==) `on` t6
(/=) = (/=) `on` t6
instance Ord a => Ord (Plucker a) where
(<) = (<) `on` t6
(>) = (>) `on` t6
(<=) = (<=) `on` t6
(>=) = (>=) `on` t6
min = pl $$ on min t6
max = pl $$ on max t6
t6 :: Elt a => Exp (Plucker a) -> Exp (a,a,a,a,a,a)
t6 (unlift -> Plucker a b c d e f) = tup6 (a,b,c,d,e,f)
pl :: Elt a => Exp (a,a,a,a,a,a) -> Exp (Plucker a)
pl (untup6 -> (a,b,c,d,e,f)) = lift (Plucker a b c d e f)
instance Num a => P.Num (Exp (Plucker a)) where
(+) = lift2 ((+) :: Plucker (Exp a) -> Plucker (Exp a) -> Plucker (Exp a))
(-) = lift2 ((-) :: Plucker (Exp a) -> Plucker (Exp a) -> Plucker (Exp a))
(*) = lift2 ((*) :: Plucker (Exp a) -> Plucker (Exp a) -> Plucker (Exp a))
negate = fmap negate
abs = fmap abs
signum = fmap signum
fromInteger x = lift (P.fromInteger x :: Plucker (Exp a))
instance Fractional a => P.Fractional (Exp (Plucker a)) where
recip = fmap recip
(/) = lift2 ((/) :: Plucker (Exp a) -> Plucker (Exp a) -> Plucker (Exp a))
fromRational x = lift (P.fromRational x :: Plucker (Exp a))
instance Floating a => P.Floating (Exp (Plucker a)) where
pi = lift (pi :: Plucker (Exp a))
exp = fmap exp
sqrt = fmap sqrt
log = fmap log
(**) = lift2 ((**) :: Plucker (Exp a) -> Plucker (Exp a) -> Plucker (Exp a))
logBase = lift2 (logBase :: Plucker (Exp a) -> Plucker (Exp a) -> Plucker (Exp a))
sin = fmap sin
tan = fmap tan
cos = fmap cos
asin = fmap asin
atan = fmap atan
acos = fmap acos
sinh = fmap sinh
tanh = fmap tanh
cosh = fmap cosh
asinh = fmap asinh
atanh = fmap atanh
acosh = fmap acosh
instance Epsilon a => Epsilon (Plucker a) where
nearZero = nearZero . quadrance
instance Functor Plucker where
fmap g (unlift -> Plucker a b c d e f) = lift (Plucker (g a) (g b) (g c) (g d) (g e) (g f))
x <$ _ = lift (Plucker x x x x x x)
type instance EltRepr LinePass = Int8
instance Elt LinePass where
eltType _ = eltType (undefined::Int8)
toElt x = let (==) = (P.==) -- -XRebindableSyntax hax
in case x of
0 -> Coplanar
1 -> Clockwise
2 -> Counterclockwise
_ -> P.error "LinePass: unhandled constructor"
fromElt Coplanar = 0
fromElt Clockwise = 1
fromElt Counterclockwise = 2
instance Eq LinePass where
x == y = bitcast x == (bitcast y :: Exp Int8)
x /= y = bitcast x /= (bitcast y :: Exp Int8)