packages feed

LslPlus-0.1.2: src/Language/Lsl/Internal/Math.hs

module Language.Lsl.Internal.Math(
    dist3d2,
    dist3d,
    mag3d2,
    mag3d,
    dot3d,
    norm3d,
    diff3d,
    add3d,
    neg3d,
    scale3d,
    rot3d,
    Permutation3(..),
    quaternionToMatrix,
    matrixToQuaternion,
    quaternionMultiply,
    invertQuaternion,
    quaternionToRotations,
    rotationsToQuaternion,
    cross,
    rotationBetween,
    angleBetween,
    axisAngleToRotation,
    axisAngleFromRotation) where

-- import Debug.Trace
dist3d2 (x1,y1,z1) (x2,y2,z2) = ((x2-x1)^2 + (y2-y1)^2 + (z2-z1)^2)
dist3d v0 v1 = sqrt $ dist3d2 v0 v1

dot3d (x1,y1,z1) (x2,y2,z2) = x1 * x2 + y1 * y2 + z1 * z2

mag3d2 (x,y,z) = (x^2 + y^2 + z^2)
mag3d v = sqrt $ mag3d2 v

norm3d v@(x,y,z) = let mag = mag3d v in (x/mag,y/mag,z/mag)

diff3d (x,y,z) (x',y',z') = (x - x', y - y', z - z')
add3d (x,y,z) (x',y',z') = (x + x', y + y', z + z')
neg3d (x,y,z) = (-x,-y,-z)

scale3d v (x,y,z) = (v*x,v*y,v*z)

quaternionToMatrix (x,y,z,s) =
    ((s*s + x*x - y*y - z*z, 2*x*y - 2*s*z,         2*x*z + 2*s*y),
     (2*x*y + 2*s*z,         s*s - x*x + y*y - z*z, 2*y*z - 2*s*x),
     (2*x*z - 2*s*y,         2*y*z + 2*s*x,         s*s - x*x - y*y + z*z))
     
-- reversing the conversion:
matrixToQuaternion ((r00,r01,r02),(r10,r11,r12),(r20,r21,r22)) =
    (sign (r21 - r12) * (sqrt (max 0 (1 + r00 - r11 - r22 ))) / 2,
     sign (r02 - r20) * (sqrt (max 0 (1 - r00 + r11 - r22))) / 2,
     sign (r10 - r01) * (sqrt (max 0 (1 - r00 - r11 + r22 ))) / 2,
     (sqrt (max 0  (1 + r00 + r11 + r22))) / 2)

quaternionMultiply (x1,y1,z1,s1) (x2,y2,z2,s2) =
    ((s1 * x2 + x1 * s2 + y1 * z2 - z1 * y2),
     (s1 * y2 - x1 * z2 + y1 * s2 + z1 * x2),
     (s1 * z2 + x1 * y2 - y1 * x2 + z1 * s2),
     (s1 * s2 - x1*x2 - y1 * y2 - z1 * z2))

rot3d vec rotation =
    (quaternionToMatrix rotation) `matMulVec` vec
    where matMulVec ((a1,b1,c1),(a2,b2,c2),(a3,b3,c3)) (a,b,c) =
              ((a1 * a + b1 * b + c1 * c),(a2 * a + b2 * b + c2 * c),(a3 * a + b3 * b + c3 * c))
    
invertQuaternion (x,y,z,s) = (-x,-y,-z,s)

rotationsToQuaternion :: Permutation3 -> (Float,Float,Float) -> (Float,Float,Float,Float)
rotationsToQuaternion order (x,y,z) = 
    let rx = (sin (x/2), 0.0, 0.0, cos (x/2))
        ry = (0.0, sin (y/2), 0.0, cos (y/2))
        rz = (0.0, 0.0, sin (z/2), cos (z/2))
        (r3,r2,r1) = permute order (rx,ry,rz)
    in ((r3 `quaternionMultiply` r2) `quaternionMultiply` r1)
    
data Permutation3 = P123 | P132 | P213 | P231 | P312 | P321

permute P123 (x,y,z) = (x,y,z)
permute P132 (x,y,z) = (x,z,y)
permute P213 (x,y,z) = (y,x,z)
permute P231 (x,y,z) = (y,z,x)
permute P312 (x,y,z) = (z,x,y)
permute P321 (x,y,z) = (z,y,x)

quatPermute (x,y,z,w) p = let (p1,p2,p3) = permute p (x,y,z) in (p1,p2,p3,w)

sign f = if f < 0 then -1 else 1

quaternionToRotations :: Permutation3 -> Bool -> (Float,Float,Float,Float) -> (Float,Float,Float)
quaternionToRotations rotOrder lh quat=
    let (p1,p2,p3,p0) = quatPermute quat rotOrder
        mult = if lh then -1 else 1
        sinTheta2 = 2 * (p0 * p2 + mult * p1 * p3)
        (sinTheta2',singularity) = 
            if (abs sinTheta2 >= 0.9999999) then (sign sinTheta2,True) else (sinTheta2,False)
        theta2 = asin sinTheta2'
        theta3 = if singularity then atan2 (p3*p0 + p1*p2) (0.5 - p1*p1 - p3*p3)
                 else atan2 (2 * (p0 * p3 - mult * p1 * p2)) (1 - 2 * (p2 * p2 + p3 * p3))
        theta1 = if singularity then 0
                 else atan2 (2 * ( p0 * p1 - mult * p2 * p3)) (1 - 2 * (p1 * p1 + p2 * p2))
    in (theta1,theta2,theta3)
    
cross (x1,y1,z1) (x2,y2,z2) = ((y1 * z2 - z1 * y2),(z1 * x2 - x1 * z2),(x1 * y2 - y1 * x2))

rotationBetween v0 v1 =
    let (v0',v1') = (norm3d v0,norm3d v1)
        axis = v0' `cross` v1'
        angle = acos (v0' `dot3d` v1') 
    in axisAngleToRotation axis angle

angleBetween (aX,aY,aZ,aS) (bX,bY,bZ,bS) =
   2 * acos ((aX * bX + aY * bY + aZ * bZ + aS * bS) / sqrt ((aX^2 + aY^2 + aZ^2 + aS^2) * (bX^2 + bY^2 + bZ^2 + bS^2)))
   
axisAngleToRotation (x,y,z) angle =
    let w = cos (angle/2.0)
        sinVal = sin (angle/2.0)
        x' = x * sinVal
        y' = y * sinVal
        z' = z * sinVal
    in (x',y',z',w)

axisAngleFromRotation (x,y,z,s) = 
    let s' = max (-1) (min 1 s)
        angle = 2 * acos s'
        sinVal = sin (angle/2)
        x' = x / sinVal
        y' = y / sinVal
        z' = z / sinVal
    in if sinVal == 0 then ((1,0,0),0) else ((x',y',z'),angle)