packages feed

moonlight-linalg-0.1.0.0: src-geometry/Moonlight/LinAlg/Pure/Geometry/Vec2.hs

module Moonlight.LinAlg.Pure.Geometry.Vec2
  ( Vec2 (..),
    Axis2 (..),
    addVec2,
    subVec2,
    scaleVec2,
    negateVec2,
    zipVec2,
    mapVec2,
    mulVec2,
    minVec2,
    maxVec2,
    vec2Zero,
    dotVec2,
    magnitudeVec2,
    normalizeVec2,
    normalizeVec2Safe,
    normalizeVec2Or,
    vec2FromList,
    vec2ToList,
    vec2ToTuple,
    maxAbsComponentVec2,
    axis2Component,
    axis2Vector,
    distanceVec2,
    averageVec2,
    rejectVec2,
    areaLikeVec2,
    perpVec2,
    crossZVec2,
  )
where

import Control.DeepSeq (NFData (..))
import Data.Kind (Type)
import Data.Vector.Generic qualified as G
import Data.Vector.Generic.Mutable qualified as M
import Data.Vector.Unboxed qualified as U
import Foreign.Storable (Storable (..), peekByteOff, pokeByteOff)
import Moonlight.Algebra.Pure.Module (BilinearSpace (..), Module (..), VectorSpace)
import Moonlight.Core (AdditiveGroup (..), AdditiveMonoid (..), Metric (..), MoonlightError (..))
import Prelude (Bounded, Double, Either (..), Enum, Eq, Int, Ord, Read, Show, abs, length, max, min, seq, show, sqrt, (*), (+), (-), (/), (<$>), (<*>), (*>), (<>), (<=))

type Axis2 :: Type
data Axis2
  = Axis2X
  | Axis2Y
  deriving stock (Eq, Ord, Show, Read, Enum, Bounded)

type Vec2 :: Type
data Vec2 = Vec2
  { vec2X :: {-# UNPACK #-} !Double,
    vec2Y :: {-# UNPACK #-} !Double
  }
  deriving stock (Eq, Ord, Show, Read)

instance NFData Vec2 where
  rnf vectorValue = vectorValue `seq` ()

instance Storable Vec2 where
  sizeOf _ = 2 * doubleStorageSize
  alignment _ = alignment (0.0 :: Double)
  peek pointerValue =
    Vec2
      <$> peekByteOff pointerValue 0
      <*> peekByteOff pointerValue doubleStorageSize
  poke pointerValue (Vec2 xValue yValue) =
    pokeByteOff pointerValue 0 xValue
      *> pokeByteOff pointerValue doubleStorageSize yValue

newtype instance U.MVector s Vec2 = MV_Vec2 (U.MVector s (Double, Double))

newtype instance U.Vector Vec2 = V_Vec2 (U.Vector (Double, Double))

instance U.Unbox Vec2

instance M.MVector U.MVector Vec2 where
  {-# INLINE basicLength #-}
  basicLength (MV_Vec2 vectorValue) = M.basicLength vectorValue

  {-# INLINE basicUnsafeSlice #-}
  basicUnsafeSlice offset lengthValue (MV_Vec2 vectorValue) =
    MV_Vec2 (M.basicUnsafeSlice offset lengthValue vectorValue)

  {-# INLINE basicOverlaps #-}
  basicOverlaps (MV_Vec2 leftVector) (MV_Vec2 rightVector) =
    M.basicOverlaps leftVector rightVector

  {-# INLINE basicUnsafeNew #-}
  basicUnsafeNew lengthValue =
    MV_Vec2 <$> M.basicUnsafeNew lengthValue

  {-# INLINE basicInitialize #-}
  basicInitialize (MV_Vec2 vectorValue) =
    M.basicInitialize vectorValue

  {-# INLINE basicUnsafeReplicate #-}
  basicUnsafeReplicate lengthValue (Vec2 xValue yValue) =
    MV_Vec2 <$> M.basicUnsafeReplicate lengthValue (xValue, yValue)

  {-# INLINE basicUnsafeRead #-}
  basicUnsafeRead (MV_Vec2 vectorValue) indexValue =
    tupleToVec2 <$> M.basicUnsafeRead vectorValue indexValue

  {-# INLINE basicUnsafeWrite #-}
  basicUnsafeWrite (MV_Vec2 vectorValue) indexValue vectorPayload =
    M.basicUnsafeWrite vectorValue indexValue (vec2ToTuple vectorPayload)

  {-# INLINE basicClear #-}
  basicClear (MV_Vec2 vectorValue) =
    M.basicClear vectorValue

  {-# INLINE basicSet #-}
  basicSet (MV_Vec2 vectorValue) vectorPayload =
    M.basicSet vectorValue (vec2ToTuple vectorPayload)

  {-# INLINE basicUnsafeCopy #-}
  basicUnsafeCopy (MV_Vec2 targetVector) (MV_Vec2 sourceVector) =
    M.basicUnsafeCopy targetVector sourceVector

  {-# INLINE basicUnsafeMove #-}
  basicUnsafeMove (MV_Vec2 targetVector) (MV_Vec2 sourceVector) =
    M.basicUnsafeMove targetVector sourceVector

  {-# INLINE basicUnsafeGrow #-}
  basicUnsafeGrow (MV_Vec2 vectorValue) lengthValue =
    MV_Vec2 <$> M.basicUnsafeGrow vectorValue lengthValue

instance G.Vector U.Vector Vec2 where
  {-# INLINE basicUnsafeFreeze #-}
  basicUnsafeFreeze (MV_Vec2 vectorValue) =
    V_Vec2 <$> G.basicUnsafeFreeze vectorValue

  {-# INLINE basicUnsafeThaw #-}
  basicUnsafeThaw (V_Vec2 vectorValue) =
    MV_Vec2 <$> G.basicUnsafeThaw vectorValue

  {-# INLINE basicLength #-}
  basicLength (V_Vec2 vectorValue) = G.basicLength vectorValue

  {-# INLINE basicUnsafeSlice #-}
  basicUnsafeSlice offset lengthValue (V_Vec2 vectorValue) =
    V_Vec2 (G.basicUnsafeSlice offset lengthValue vectorValue)

  {-# INLINE basicUnsafeIndexM #-}
  basicUnsafeIndexM (V_Vec2 vectorValue) indexValue =
    tupleToVec2 <$> G.basicUnsafeIndexM vectorValue indexValue

  {-# INLINE basicUnsafeCopy #-}
  basicUnsafeCopy (MV_Vec2 targetVector) (V_Vec2 sourceVector) =
    G.basicUnsafeCopy targetVector sourceVector

  {-# INLINE elemseq #-}
  elemseq _ (Vec2 xValue yValue) resultValue =
    G.elemseq doubleVectorWitness xValue (G.elemseq doubleVectorWitness yValue resultValue)

instance AdditiveMonoid Vec2 where
  zero = Vec2 0.0 0.0
  add (Vec2 x1 y1) (Vec2 x2 y2) = Vec2 (x1 + x2) (y1 + y2)

instance AdditiveGroup Vec2 where
  neg (Vec2 xValue yValue) = Vec2 (-xValue) (-yValue)

instance Module Double Vec2 where
  scale scaleValue (Vec2 xValue yValue) = Vec2 (scaleValue * xValue) (scaleValue * yValue)

instance VectorSpace Double Vec2

instance BilinearSpace Double Vec2 where
  bilinearForm (Vec2 x1 y1) (Vec2 x2 y2) = x1 * x2 + y1 * y2

instance Metric Vec2 where
  type Magnitude Vec2 = Double
  magnitude vectorValue = sqrt (bilinearForm vectorValue vectorValue)

vec2Zero :: Vec2
vec2Zero = zero

addVec2 :: Vec2 -> Vec2 -> Vec2
addVec2 = add

subVec2 :: Vec2 -> Vec2 -> Vec2
subVec2 = sub

negateVec2 :: Vec2 -> Vec2
negateVec2 = neg

scaleVec2 :: Double -> Vec2 -> Vec2
scaleVec2 = scale

zipVec2 :: (Double -> Double -> Double) -> Vec2 -> Vec2 -> Vec2
zipVec2 combine (Vec2 x1 y1) (Vec2 x2 y2) =
  Vec2 (combine x1 x2) (combine y1 y2)

mapVec2 :: (Double -> Double) -> Vec2 -> Vec2
mapVec2 transform (Vec2 xValue yValue) =
  Vec2 (transform xValue) (transform yValue)

mulVec2 :: Vec2 -> Vec2 -> Vec2
mulVec2 = zipVec2 (*)

minVec2 :: Vec2 -> Vec2 -> Vec2
minVec2 = zipVec2 min

maxVec2 :: Vec2 -> Vec2 -> Vec2
maxVec2 = zipVec2 max

dotVec2 :: Vec2 -> Vec2 -> Double
dotVec2 = bilinearForm

magnitudeVec2 :: Vec2 -> Double
magnitudeVec2 = magnitude

normalizeVec2 :: Vec2 -> Either MoonlightError Vec2
normalizeVec2 vectorValue =
  let vectorMagnitude = magnitudeVec2 vectorValue
   in if vectorMagnitude <= 1.0e-12
        then Left (InvariantViolation "member direction cannot be normalized from a zero-length vector")
        else Right (scaleVec2 (1.0 / vectorMagnitude) vectorValue)

normalizeVec2Safe :: Vec2 -> Vec2
normalizeVec2Safe vectorValue =
  let vectorMagnitude = magnitudeVec2 vectorValue
   in if vectorMagnitude <= 1.0e-12 then vec2Zero else scaleVec2 (1.0 / vectorMagnitude) vectorValue

normalizeVec2Or :: Vec2 -> Vec2 -> Vec2
normalizeVec2Or fallback vectorValue =
  let vectorMagnitude = magnitudeVec2 vectorValue
   in if vectorMagnitude <= 1.0e-12 then fallback else scaleVec2 (1.0 / vectorMagnitude) vectorValue

vec2FromList :: [Double] -> Either MoonlightError Vec2
vec2FromList values =
  case values of
    [xValue, yValue] -> Right (Vec2 xValue yValue)
    _ ->
      Left
        ( InvariantViolation
            ( "Vec2 requires exactly 2 entries, received "
                <> show (length values)
            )
        )

vec2ToList :: Vec2 -> [Double]
vec2ToList (Vec2 xValue yValue) = [xValue, yValue]

vec2ToTuple :: Vec2 -> (Double, Double)
vec2ToTuple (Vec2 xValue yValue) = (xValue, yValue)

tupleToVec2 :: (Double, Double) -> Vec2
tupleToVec2 (xValue, yValue) = Vec2 xValue yValue

doubleStorageSize :: Int
doubleStorageSize = sizeOf (0.0 :: Double)

doubleVectorWitness :: U.Vector Double
doubleVectorWitness = U.empty

maxAbsComponentVec2 :: Vec2 -> Double
maxAbsComponentVec2 (Vec2 xValue yValue) =
  max (abs xValue) (abs yValue)

axis2Component :: Axis2 -> Vec2 -> Double
axis2Component Axis2X (Vec2 xValue _) = xValue
axis2Component Axis2Y (Vec2 _ yValue) = yValue

axis2Vector :: Axis2 -> Double -> Vec2
axis2Vector Axis2X magnitudeValue = Vec2 magnitudeValue 0.0
axis2Vector Axis2Y magnitudeValue = Vec2 0.0 magnitudeValue

distanceVec2 :: Vec2 -> Vec2 -> Double
distanceVec2 leftPosition rightPosition =
  magnitudeVec2 (subVec2 leftPosition rightPosition)

averageVec2 :: Vec2 -> Vec2 -> Vec2
averageVec2 leftValue rightValue =
  scaleVec2 0.5 (addVec2 leftValue rightValue)

rejectVec2 :: Vec2 -> Vec2 -> Vec2
rejectVec2 onto vectorValue =
  subVec2 vectorValue (scaleVec2 (dotVec2 onto vectorValue) onto)

areaLikeVec2 :: Vec2 -> Double
areaLikeVec2 (Vec2 xValue yValue) = abs (xValue * yValue)

perpVec2 :: Vec2 -> Vec2
perpVec2 (Vec2 xValue yValue) = Vec2 (-yValue) xValue

crossZVec2 :: Vec2 -> Vec2 -> Double
crossZVec2 (Vec2 x1 y1) (Vec2 x2 y2) = x1 * y2 - y1 * x2