packages feed

brush-strokes-0.1.0.0: src/lib/Math/Module.hs

{-# LANGUAGE AllowAmbiguousTypes  #-}
{-# LANGUAGE ScopedTypeVariables  #-}
{-# LANGUAGE TemplateHaskell      #-}
{-# LANGUAGE UndecidableInstances #-}

{-# OPTIONS_GHC -Wno-orphans #-}

module Math.Module
  ( Module(..), lerp
  , Inner(..), Cross(..)
  , Interpolatable
  , norm, squaredNorm, normalise
  , quadrance, distance
  , proj, projC, closestPointOnSegment
  , strictlyParallel, convexCombination

  , ViaModule(..)
  )
  where

-- base
import Control.Monad
  ( guard )
import Data.Coerce
  ( coerce )
import Data.Kind
  ( Type, Constraint )
import Data.Monoid
  ( Ap(..), Sum(..) )

-- acts
import Data.Act
  ( Act
    ( (•) )
  , Torsor
    ( (-->) )
  )

-- groups
import Data.Group
  ( Group(..) )

-- brush-strokes
import Math.Epsilon
  ( epsilon )
import Math.Linear
import Math.Module.Internal
  ( Module(..), Inner(..) )
import Math.Ring
  ( Ring )
import qualified Math.Ring as Ring

--------------------------------------------------------------------------------

instance ( Applicative f, Module r m ) => Module r ( Ap f m ) where
  origin = pure origin
  (^+^)  = liftA2 (^+^)
  (^-^)  = liftA2 (^-^)
  (*^) r = fmap ( r *^ )

lerp :: forall v r p. ( Module r v, Torsor v p ) => r -> p -> p -> p
lerp t p0 p1 = ( t *^ ( p0 --> p1 :: v ) ) • p0

class Module r m => Cross r m where
  (×) :: m -> m -> r

-- | Norm of a vector, computed using the inner product.
norm :: forall m r. ( Floating r, Inner r m ) => m -> r
norm = sqrt . squaredNorm

-- | Squared norm of a vector, computed using the inner product.
squaredNorm :: forall m r. Inner r m => m -> r
squaredNorm v = v ^.^ v

normalise :: ( Floating r, Inner r m ) => m -> m
normalise v = recip ( norm v ) *^ v

-- | Squared distance between two points.
quadrance :: forall v r p. ( Inner r v, Torsor v p ) => p -> p -> r
quadrance p1 p2 = squaredNorm ( p1 --> p2 :: v )

-- | Distance between two points.
distance :: forall v r p. ( Floating r, Inner r v, Torsor v p ) => p -> p -> r
distance p1 p2 = sqrt ( quadrance @v p1 p2 )

-- | Projects the first argument onto the second.
proj :: forall m r. ( Inner r m, Fractional r ) => m -> m -> m
proj x y = projC x y *^ y

-- | Projection constant: how far along the projection of the first vector lands along the second vector.
projC :: forall m r. ( Inner r m, Fractional r ) => m -> m -> r
projC x y = x ^.^ y / squaredNorm y

closestPointOnSegment
  :: forall v r p
  . ( Inner r v, Torsor v p, Fractional r, Ord r )
  => p -> Segment p -> ( r, p )
closestPointOnSegment c ( Segment p0 p1 )
  | t <= 0
  = ( 0, p0 )
  | t >= 1
  = ( 1, p1 )
  | otherwise
  = ( t, ( t *^ v01 ) • p0 )
  where
    v01 :: v
    v01 = p0 --> p1
    t :: r
    t = projC ( p0 --> c ) v01

--------------------------------------------------------------------------------

-- | A convenient constraint synonym for types that support interpolation.
type Interpolatable :: Type -> Type -> Constraint
class    ( Torsor ( T u ) u, Module r ( T u ) ) => Interpolatable r u
instance ( Torsor ( T u ) u, Module r ( T u ) ) => Interpolatable r u

--------------------------------------------------------------------------------

instance Ring a => Inner a ( Sum a ) where
  Sum a ^.^ Sum b = a Ring.* b

instance Inner Double ( T ( ℝ 2 ) ) where
  V2 x1 y1 ^.^ V2 x2 y2 = x1 Ring.* x2 + y1 Ring.* y2

instance Cross Double ( T ( ℝ 2 ) ) where
  V2 x1 y1 × V2 x2 y2 = x1 Ring.* y2 Ring.- x2 Ring.* y1

-- | Compute whether two vectors point in the same direction,
-- that is, whether each vector is a (strictly) positive multiple of the other.
--
-- Returns @False@ if either of the vectors is zero (or very close to zero).
strictlyParallel :: T ( ℝ 2 ) -> T ( ℝ 2 ) -> Bool
strictlyParallel u v
  =  abs ( u  ×  v ) < tol -- vectors are collinear
  &&       u ^.^ v   > tol -- vectors point in the same direction (parallel and not anti-parallel)
  where
    tol = norm u * norm v * epsilon

-- | Finds whether the query vector @ u @ is a convex combination of the two provided vectors @ v0 @, @ v1 @.
--
-- If so, returns @ t @ in @ [ 0, 1 ] @ such that @ ( 1 - t ) v0 + t v1 @ is a positive multiple of @ u @.
convexCombination
  :: T ( ℝ 2 ) -- ^ first vector
  -> T ( ℝ 2 ) -- ^ second vector
  -> T ( ℝ 2 ) -- ^ query vector
  -> Maybe Double
convexCombination v0 v1 u
  | abs c10 < epsilon
  = if strictlyParallel u v0
    then Just 0
    else if strictlyParallel u v1
      then Just 1
      else Nothing
  | otherwise
  = do
      let
        t :: Double
        t = c0 / c10
      guard ( t > -epsilon && t < 1 + epsilon )
      guard ( epsilon < u ^.^ ( lerp @( T ( ℝ 2 ) ) t v0 v1 ) )
      Just $ min 1 ( max 0 t )

  where
    c0, c10 :: Double
    c0  = v0 × u
    c10 = ( v0 ^-^ v1 ) × u

--------------------------------------------------------------------------------
-- Not sure how to set things up to automate the following...

instance Module Double ( T ( ℝ 0 ) ) where
  origin      = T $$( tabulateQ \ _ -> [|| unT $ origin ||] )
  T a ^+^ T b = T $$( tabulateQ \ i -> [|| unT $ T $$( indexQ [|| a ||] i ) ^+^ T $$( indexQ [|| b ||] i ) ||] )
  T a ^-^ T b = T $$( tabulateQ \ i -> [|| unT $ T $$( indexQ [|| a ||] i ) ^-^ T $$( indexQ [|| b ||] i ) ||] )
  k *^ T a    = T $$( tabulateQ \ i -> [|| unT $ k *^ T $$( indexQ [|| a ||] i ) ||] )

instance Module Double ( T ( ℝ 1 ) ) where
  origin      = T $$( tabulateQ \ _ -> [|| unT $ origin ||] )
  T a ^+^ T b = T $$( tabulateQ \ i -> [|| unT $ T $$( indexQ [|| a ||] i ) ^+^ T $$( indexQ [|| b ||] i ) ||] )
  T a ^-^ T b = T $$( tabulateQ \ i -> [|| unT $ T $$( indexQ [|| a ||] i ) ^-^ T $$( indexQ [|| b ||] i ) ||] )
  k *^ T a    = T $$( tabulateQ \ i -> [|| unT $ k *^ T $$( indexQ [|| a ||] i ) ||] )

instance Module Double ( T ( ℝ 2 ) ) where
  origin      = T $$( tabulateQ \ _ -> [|| unT $ origin ||] )
  T a ^+^ T b = T $$( tabulateQ \ i -> [|| unT $ T $$( indexQ [|| a ||] i ) ^+^ T $$( indexQ [|| b ||] i ) ||] )
  T a ^-^ T b = T $$( tabulateQ \ i -> [|| unT $ T $$( indexQ [|| a ||] i ) ^-^ T $$( indexQ [|| b ||] i ) ||] )
  k *^ T a    = T $$( tabulateQ \ i -> [|| unT $ k *^ T $$( indexQ [|| a ||] i ) ||] )

instance Module Double ( T ( ℝ 3 ) ) where
  origin      = T $$( tabulateQ \ _ -> [|| unT $ origin ||] )
  T a ^+^ T b = T $$( tabulateQ \ i -> [|| unT $ T $$( indexQ [|| a ||] i ) ^+^ T $$( indexQ [|| b ||] i ) ||] )
  T a ^-^ T b = T $$( tabulateQ \ i -> [|| unT $ T $$( indexQ [|| a ||] i ) ^-^ T $$( indexQ [|| b ||] i ) ||] )
  k *^ T a    = T $$( tabulateQ \ i -> [|| unT $ k *^ T $$( indexQ [|| a ||] i ) ||] )

instance Module Double ( T ( ℝ 4 ) ) where
  origin      = T $$( tabulateQ \ _ -> [|| unT $ origin ||] )
  T a ^+^ T b = T $$( tabulateQ \ i -> [|| unT $ T $$( indexQ [|| a ||] i ) ^+^ T $$( indexQ [|| b ||] i ) ||] )
  T a ^-^ T b = T $$( tabulateQ \ i -> [|| unT $ T $$( indexQ [|| a ||] i ) ^-^ T $$( indexQ [|| b ||] i ) ||] )
  k *^ T a    = T $$( tabulateQ \ i -> [|| unT $ k *^ T $$( indexQ [|| a ||] i ) ||] )

deriving via ViaModule Double ( T ( ℝ n ) )
  instance Module Double ( T ( ℝ n ) ) => Semigroup ( T ( ℝ n ) )
deriving via ViaModule Double ( T ( ℝ n ) )
  instance Module Double ( T ( ℝ n ) ) => Monoid ( T ( ℝ n ) )
deriving via ViaModule Double ( T ( ℝ n ) )
  instance Module Double ( T ( ℝ n ) ) => Group ( T ( ℝ n ) )

deriving via ViaModule Double ( ℝ n )
  instance Module Double ( T ( ℝ n ) ) => Act ( T ( ℝ n ) ) ( ℝ n )
deriving via ( ViaModule Double ( ℝ n ) )
  instance Module Double ( T ( ℝ n ) ) => Torsor ( T ( ℝ n ) ) ( ℝ n )

--------------------------------------------------------------------------------

newtype ViaModule r m = ViaModule { unViaModule :: m }

instance Module r m => Semigroup ( ViaModule r m ) where
  (<>) = coerce $ (^+^) @r @m
instance Module r m => Monoid ( ViaModule r m ) where
  mempty = coerce $ origin @r @m
instance Module r m => Group ( ViaModule r m ) where
  invert = coerce $ ( \ x -> (^-^) @r @m ( origin @r @m ) x )

instance ( Semigroup ( T m ), Module r ( T m ) ) => Act    ( T m ) ( ViaModule r m ) where
  g • ViaModule m = ViaModule ( unT $ g ^+^ T m )
instance ( Group     ( T m ), Module r ( T m ) ) => Torsor ( T m ) ( ViaModule r m ) where
  ViaModule a --> ViaModule b = T ( unT $ T b ^-^ T a )