module Math.Grads.Angem.Internal.MatrixOperations
( alignmentFunc
, rotation2D
) where
import Linear.Matrix (M22, det22,
transpose, (!*!),
(*!))
import Linear.V2 (V2 (..))
import Linear.Vector (negated, (^+^),
(^-^))
import Math.Grads.Angem.Internal.VectorOperations (avg)
alignmentFunc :: [V2 Float] -> [V2 Float] -> V2 Float -> V2 Float
alignmentFunc points1 points2 = transformFunc
where
(rotationM, transitionV) = superImpose points1 points2
transformFunc = transform rotationM transitionV
superImpose :: [V2 Float] -> [V2 Float] -> (M22 Float, V2 Float)
superImpose points1 points2 = (rotation, transition)
where
(avg1, moved1) = moveToCenter points1
(avg2, moved2) = moveToCenter points2
aMatrix = transpose moved2 !*! moved1
(u, vt) = svd aMatrix
rotation' = rotationMatrix vt u
rotation = if det22 rotation' >= 0
then rotation'
else case vt of
(V2 v1 v2) -> rotationMatrix (V2 v1 (negated v2)) u
transition = avg1 - (avg2 *! rotation)
svd :: M22 Float -> (M22 Float, M22 Float)
svd aMatrix' = (doubleToFloatM22 rotationA, doubleToFloatM22 rotationB)
where
V2 (V2 a b) (V2 c d) = floatToDoubleM22 aMatrix'
e = (a + d) / 2
f = (a - d) / 2
g = (c + b) / 2
h = (c - b) / 2
q = sqrt (e ** 2 + h ** 2)
r = sqrt (f ** 2 + g ** 2)
a1 = atan2 g f
a2 = atan2 h e
sy = q - r
s = if sy < 0 then -1 else 1
theta = (a2 - a1) / 2
phi = (a2 + a1) / 2
rotationA = V2 (V2 (cos phi) (- s * sin phi)) (V2 (sin phi) (s * cos phi))
rotationB = V2 (V2 (cos theta) (- sin theta)) (V2 (sin theta) (cos theta))
moveToCenter :: [V2 Float] -> (V2 Float, [V2 Float])
moveToCenter points = (avgPoint, movedPoints)
where
avgPoint = avg points
movedPoints = (^-^ avgPoint) <$> points
rotationMatrix :: M22 Float -> M22 Float -> M22 Float
rotationMatrix vt u = transpose $ transpose vt !*! transpose u
rotation2D :: Float -> M22 Float
rotation2D angle = V2 (V2 (cos trueAngle) (- sin trueAngle)) (V2 (sin trueAngle) (cos trueAngle))
where
trueAngle = 2 * pi * angle / 360.0
transform :: M22 Float -> V2 Float -> V2 Float-> V2 Float
transform rotationM transitionV = convFunc
where
convFunc = transformVector rotationM transitionV
transformVector :: M22 Float -> V2 Float -> V2 Float-> V2 Float
transformVector rotationM transitionV v = (v *! rotationM) ^+^ transitionV
doubleToFloatM22 :: M22 Double -> M22 Float
doubleToFloatM22 (V2 a' b') = V2 (realToFrac <$> a') (realToFrac <$> b')
floatToDoubleM22 :: M22 Float -> M22 Double
floatToDoubleM22 (V2 a' b') = V2 (realToFrac <$> a') (realToFrac <$> b')