easytensor-2.1.0.0: test/Numeric/MatrixFloatTest.hs
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeOperators #-}
module Numeric.MatrixFloatTest (runTests) where
import Data.Fixed
import Numeric.Arbitraries
import Numeric.DataFrame
import Numeric.Dimensions
import Test.QuickCheck
type TestElem = Float
dropW :: (SubSpace t '[3] '[] '[3], SubSpace t '[4] '[] '[4])
=> Vector t 4 -> Vector t 3
dropW (Vec4 x y z _) = Vec3 x y z
prop_detTranspose :: SomeSquareMatrix AnyMatrix TestElem -> Property
prop_detTranspose (SSM m') = counterexample
(unlines
[ "Failed det/transpose:"
, "m: " ++ show m
, "mT: " ++ show (transpose m)
]
) $ approxEq mag a b
where
n2 = totalDim (dims `inSpaceOf` m)
mag = fromIntegral (n2*n2)
mm = recip $ maxElem m' `max` 1
m = ewmap (*mm) m'
a = det m
b = det $ transpose m
prop_inverse :: SomeSquareMatrix NonSingular TestElem -> Property
prop_inverse (SSM m) = check (m %* mi) .&&. check (mi %* m)
where
n2 = totalDim (dims `inSpaceOf` m)
mag = maxElem m `max` maxElem mi `max` 1
* fromIntegral (n2*n2)
mi = inverse m
check a = counterexample
( unlines
[ "Failed inverse:"
, " m: " ++ show m
, " mi: " ++ show mi
, " eye': " ++ show a
]
) $ approxEq mag a eye
prop_translate3vs4 :: Vector TestElem 4 -> Bool
prop_translate3vs4 v = translate4 v == translate3 (dropW v)
prop_translate4 :: Vector TestElem 4 -> Vector TestElem 3 -> Bool
prop_translate4 a b = toHomPoint b %* translate4 a == toHomPoint (dropW a + b)
prop_translate3 :: Vector TestElem 3 -> Vector TestElem 3 -> Bool
prop_translate3 a b = toHomPoint b %* translate3 a == toHomPoint (a + b)
prop_rotateX :: Vector TestElem 4 -> Property
prop_rotateX v@(Vec4 x y z w) =
conjoin [
v %* rotateX (-2 * pi) =~= v,
v %* rotateX (-1.5 * pi) =~= vec4 x (-z) y w,
v %* rotateX (-pi) =~= vec4 x (-y) (-z) w,
v %* rotateX (-0.5 * pi) =~= vec4 x z (-y) w,
v %* rotateX 0 =~= v,
v %* rotateX (0.5 * pi) =~= vec4 x (-z) y w,
v %* rotateX pi =~= vec4 x (-y) (-z) w,
v %* rotateX (1.5 * pi) =~= vec4 x z (-y) w,
v %* rotateX (2 * pi) =~= v
]
prop_rotateY :: Vector TestElem 4 -> Property
prop_rotateY v@(Vec4 x y z w) =
conjoin [
v %* rotateY (-2 * pi) =~= v,
v %* rotateY (-1.5 * pi) =~= vec4 z y (-x) w,
v %* rotateY (-pi) =~= vec4 (-x) y (-z) w,
v %* rotateY (-0.5 * pi) =~= vec4 (-z) y x w,
v %* rotateY 0 =~= v,
v %* rotateY (0.5 * pi) =~= vec4 z y (-x) w,
v %* rotateY pi =~= vec4 (-x) y (-z) w,
v %* rotateY (1.5 * pi) =~= vec4 (-z) y x w,
v %* rotateY (2 * pi) =~= v
]
prop_rotateZ :: Vector TestElem 4 -> Property
prop_rotateZ v@(Vec4 x y z w) =
conjoin [
v %* rotateZ (-2 * pi) =~= v,
v %* rotateZ (-1.5 * pi) =~= vec4 (-y) x z w,
v %* rotateZ (-pi) =~= vec4 (-x) (-y) z w,
v %* rotateZ (-0.5 * pi) =~= vec4 y (-x) z w,
v %* rotateZ 0 =~= v,
v %* rotateZ (0.5 * pi) =~= vec4 (-y) x z w,
v %* rotateZ pi =~= vec4 (-x) (-y) z w,
v %* rotateZ (1.5 * pi) =~= vec4 y (-x) z w,
v %* rotateZ (2 * pi) =~= v
]
prop_rotate :: TestElem -> Property
prop_rotate a =
conjoin [
rotate (vec3 1 0 0) a =~= rotateX a,
rotate (vec3 0 1 0) a =~= rotateY a,
rotate (vec3 0 0 1) a =~= rotateZ a
]
prop_rotateEuler :: TestElem -> TestElem -> TestElem -> Property
prop_rotateEuler pitch yaw roll =
rotateEuler pitch yaw roll =~= rotateZ roll %* rotateY yaw %* rotateX pitch
prop_lookAt :: Vector TestElem 3 -> Vector TestElem 3 -> Vector TestElem 3 -> Property
prop_lookAt up cam foc =
(apart cam foc && nonColinear up zb && nonColinear zb xb) ==>
conjoin [
(normalized . fromHom $ toHomPoint foc %* m) `aeq` vec3 0 0 (-1),
fromHom (toHomPoint cam %* m) `aeq` 0,
fromHom (toHomVector xb %* m) `aeq` vec3 1 0 0,
fromHom (toHomVector yb %* m) `aeq` vec3 0 1 0,
fromHom (toHomVector zb %* m) `aeq` vec3 0 0 1
]
where
aeq :: Vector TestElem 3 -> Vector TestElem 3 -> Property
aeq = approxEq 10
apart :: Vector TestElem 3 -> Vector TestElem 3 -> Bool
apart a b = normL2 (a - b) > max (2 * sqrt M_EPS) (0.1 * (normL2 a + normL2 b))
nonColinear :: Vector TestElem 3 -> Vector TestElem 3 -> Bool
nonColinear a b = normL2 (cross a b) > 0.1 * abs (dot a b)
m = lookAt up cam foc
zb = normalized $ cam - foc
xb = normalized $ up `cross` zb
yb = zb `cross` xb
prop_perspective :: TestElem -> TestElem -> TestElem -> TestElem -> Property
prop_perspective a b c d =
conjoin [
projectTo 0 0 n =~= vec3 0 0 (-1),
projectTo 0 0 f =~= vec3 0 0 1,
projectTo 1 1 n =~= vec3 1 1 (-1),
projectTo 1 (-1) n =~= vec3 1 (-1) (-1),
projectTo (-1) 1 n =~= vec3 (-1) 1 (-1),
projectTo (-1) (-1) n =~= vec3 (-1) (-1) (-1),
projectTo 1 1 f =~= vec3 1 1 1,
projectTo 1 (-1) f =~= vec3 1 (-1) 1,
projectTo (-1) 1 f =~= vec3 (-1) 1 1,
projectTo (-1) (-1) f =~= vec3 (-1) (-1) 1
]
where
n = 1.0 + mod' a 9.0 -- Near plane in range [1, 10)
f = 2*n + mod' b 99.0 -- Far plane in range [2*n, 2*n + 100)
fovy = (0.1 * pi) + mod' c (0.8 * pi) -- Y-axis field of view in range [0.1*pi, 0.9*pi)
aspect = 0.25 + mod' d 4.0 -- Aspect ration in range [1/4, 4/1]
hpd = tan (fovy * 0.5) -- height/distance
wpd = aspect * hpd -- width/distance
m = perspective n f fovy aspect
projectTo x' y' z = fromHom $ vec4 (x' * wpd * z) (y' * hpd * z) (-z) 1 %* m
prop_orthogonal :: TestElem -> TestElem -> TestElem -> TestElem -> Property
prop_orthogonal a b c d =
conjoin [
projectTo 0 0 n =~= vec3 0 0 (-1),
projectTo 0 0 f =~= vec3 0 0 1,
projectTo 1 1 n =~= vec3 1 1 (-1),
projectTo 1 (-1) n =~= vec3 1 (-1) (-1),
projectTo (-1) 1 n =~= vec3 (-1) 1 (-1),
projectTo (-1) (-1) n =~= vec3 (-1) (-1) (-1),
projectTo 1 1 f =~= vec3 1 1 1,
projectTo 1 (-1) f =~= vec3 1 (-1) 1,
projectTo (-1) 1 f =~= vec3 (-1) 1 1,
projectTo (-1) (-1) f =~= vec3 (-1) (-1) 1
]
where
n = 1.0 + mod' a 9.0 -- Near plane in range [1, 10)
f = 2*n + mod' b 99.0 -- Far plane in range [2*n, 2*n + 100)
w = 1.0 + mod' c 9999.0 -- Width in range [1, 10000)
h = 1.0 + mod' d 9999.0 -- Height in range [1, 10000)
m = orthogonal n f w h
projectTo x' y' z = fromHom $ vec4 (x' * w * 0.5) (y' * h * 0.5) (-z) 1 %* m
prop_toHomPoint :: Vector TestElem 3 -> Bool
prop_toHomPoint v@(Vec3 x y z) = toHomPoint v == vec4 x y z 1
prop_toHomVector :: Vector TestElem 3 -> Bool
prop_toHomVector v@(Vec3 x y z) = toHomVector v == vec4 x y z 0
prop_fromHom :: Vector TestElem 4 -> Property
prop_fromHom v@(Vec4 x y z w) =
case w of
0 -> fromHom v === vec3 x y z
_ -> fromHom v =~= vec3 (x/w) (y/w) (z/w)
return []
runTests :: Int -> IO Bool
runTests n = $forAllProperties
$ quickCheckWithResult stdArgs { maxSuccess = n }