packages feed

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

{-# LANGUAGE LambdaCase #-}

module Moonlight.LinAlg.Pure.Geometry.Frame
  ( OrthonormalFrame,
    identityOrthonormalFrame,
    orthonormalFrameColumns,
    orthonormalFrameFromColumns,
    orthonormalFrameFromMatrixEntries,
  )
where

import Control.DeepSeq (NFData (..))
import Data.Kind (Type)
import Moonlight.LinAlg.Pure.Geometry.Vec3
  ( Vec3 (..),
    dotVec3,
    magnitudeVec3,
  )
import Prelude (Bool, Double, Eq, Maybe (..), Read, Show, abs, all, seq, (-), (&&), (<=))

type OrthonormalFrame :: Type
data OrthonormalFrame
  = OrthonormalFrame
      {-# UNPACK #-} !Vec3
      {-# UNPACK #-} !Vec3
      {-# UNPACK #-} !Vec3
  deriving stock (Eq, Show, Read)

instance NFData OrthonormalFrame where
  rnf frameValue = frameValue `seq` ()

identityOrthonormalFrame :: OrthonormalFrame
identityOrthonormalFrame =
  OrthonormalFrame
    (Vec3 1.0 0.0 0.0)
    (Vec3 0.0 1.0 0.0)
    (Vec3 0.0 0.0 1.0)

orthonormalFrameColumns :: OrthonormalFrame -> (Vec3, Vec3, Vec3)
orthonormalFrameColumns (OrthonormalFrame axis1 axis2 axis3) = (axis1, axis2, axis3)

orthonormalFrameFromColumns :: [Vec3] -> Maybe OrthonormalFrame
orthonormalFrameFromColumns = \case
  axis1 : axis2 : axis3 : []
    | isOrthonormalTriple axis1 axis2 axis3 ->
        Just (OrthonormalFrame axis1 axis2 axis3)
  _ ->
    Nothing

orthonormalFrameFromMatrixEntries :: [Double] -> Maybe OrthonormalFrame
orthonormalFrameFromMatrixEntries = \case
  [ r1c1,
    r1c2,
    r1c3,
    r2c1,
    r2c2,
    r2c3,
    r3c1,
    r3c2,
    r3c3
    ] ->
      orthonormalFrameFromColumns
        [ Vec3 r1c1 r2c1 r3c1,
          Vec3 r1c2 r2c2 r3c2,
          Vec3 r1c3 r2c3 r3c3
        ]
  _ ->
    Nothing

isOrthonormalTriple :: Vec3 -> Vec3 -> Vec3 -> Bool
isOrthonormalTriple axis1 axis2 axis3 =
  all isUnitVector [axis1, axis2, axis3]
    && all
      orthogonalWithinTolerance
      [ (axis1, axis2),
        (axis1, axis3),
        (axis2, axis3)
      ]

isUnitVector :: Vec3 -> Bool
isUnitVector axisValue =
  abs (magnitudeVec3 axisValue - 1.0) <= frameTolerance

orthogonalWithinTolerance :: (Vec3, Vec3) -> Bool
orthogonalWithinTolerance (leftAxis, rightAxis) =
  abs (dotVec3 leftAxis rightAxis) <= frameTolerance

frameTolerance :: Double
frameTolerance = 1.0e-10