packages feed

geomancy-0.4.0.0: test/Support/Gen.hs

{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE ImportQualifiedPost #-}

module Support.Gen
  ( genFloat
  , genDouble
  , genAngle
  , genAngleD
  , genVec2
  , genVec3
  , genVec4
  , genSmallVec3
  , genDVec2
  , genDVec3
  , genDVec4
  , genSmallDVec3
  , genDQuaternion
  , genInt32
  , genWord32
  , genInt64
  , genWord64
  , genEdgeScalar
  , genMostlySame
  , genTransform
  , forAllTransform
  , forAllDTransform
  ) where

import Geomancy
import Hedgehog

import Control.Monad (when)
import Data.Int (Int32, Int64)
import Data.Word (Word32, Word64)
import GHC.Stack (withFrozenCallStack)
import Hedgehog.Gen qualified as Gen
import Hedgehog.Range qualified as Range
import Linear qualified
import Text.Printf (printf)

import Geomancy.DMat4 qualified
import Geomancy.Mat4 qualified
import Geomancy.Transform qualified as Transform

import Support.Linear (toM44, toM44D)

forAllTransform :: PropertyT IO (Geomancy.Mat4.Mat4, Linear.M44 Float)
forAllTransform = withFrozenCallStack do
  (_name, Transform g) <- forAllWith fst genTransform
  pure (g, toM44 g)

forAllDTransform :: PropertyT IO (Geomancy.DMat4.DMat4, Linear.M44 Double)
forAllDTransform = withFrozenCallStack do
  (_name, t) <- forAllWith fst genTransform
  let g = Geomancy.DMat4.fromMat4 t
  pure (g, toM44D g)

genInt32 :: Gen Int32
genInt32 = Gen.int32 Range.linearBounded

genWord32 :: Gen Word32
genWord32 = Gen.word32 Range.linearBounded

genInt64 :: Gen Int64
genInt64 = Gen.int64 Range.linearBounded

genWord64 :: Gen Word64
genWord64 = Gen.word64 Range.linearBounded

genEdgeScalar :: Fractional a => Gen a
genEdgeScalar = Gen.frequency [(4, pure 0), (4, pure (-0)), (4, pure 1), (4, pure (-1)), (1, pure (0 / 0))]

genMostlySame :: Fractional a => Int -> a -> Gen a
genMostlySame sameWeight x = Gen.frequency [(sameWeight, pure x), (1, genEdgeScalar)]

genFloat :: Gen Float
genFloat = Gen.float (Range.linearFracFrom 0.0 (-1e6) 1e6)

genDouble :: Gen Double
genDouble = Gen.double (Range.linearFracFrom 0.0 (-1e6) 1e6)

genVec2 :: Gen Vec2
genVec2 = vec2 <$> genFloat <*> genFloat

genVec4 :: Gen Vec4
genVec4 = vec4 <$> genFloat <*> genFloat <*> genFloat <*> genFloat

genDVec2 :: Gen DVec2
genDVec2 = dvec2 <$> genDouble <*> genDouble

genDVec3 :: Gen DVec3
genDVec3 = dvec3 <$> genDouble <*> genDouble <*> genDouble

genDVec4 :: Gen DVec4
genDVec4 = dvec4 <$> genDouble <*> genDouble <*> genDouble <*> genDouble

genSmallVec3 :: Gen Vec3
genSmallVec3 =
  vec3
    <$> Gen.float (Range.linearFracFrom 0.0 (-10) 10)
    <*> Gen.float (Range.linearFracFrom 0.0 (-10) 10)
    <*> Gen.float (Range.linearFracFrom 0.0 (-10) 10)

genSmallDVec3 :: Gen DVec3
genSmallDVec3 =
  dvec3
    <$> Gen.double (Range.linearFracFrom 0.0 (-10) 10)
    <*> Gen.double (Range.linearFracFrom 0.0 (-10) 10)
    <*> Gen.double (Range.linearFracFrom 0.0 (-10) 10)

genDQuaternion :: Gen DQuaternion
genDQuaternion = dquaternion <$> genDouble <*> genDouble <*> genDouble <*> genDouble

genAngleD :: Gen Double
genAngleD = Gen.double (Range.linearFracFrom 0.0 (-8*pi) (8*pi))

genVec3 :: Gen Vec3
genVec3 =
  vec3
    <$> Gen.float (Range.linearFracFrom 0.0 (-1e6) 1e6)
    <*> Gen.float (Range.linearFracFrom 0.0 (-1e6) 1e6)
    <*> Gen.float (Range.linearFracFrom 0.0 (-1e6) 1e6)

genAngle :: Gen Float
genAngle = Gen.float (Range.linearFracFrom 0.0 (-8*pi) (8*pi))

genTransform :: Gen ([Char], Transform) -- TODO: cross-check with Linear
genTransform = Gen.choice
  [ genIdentity
  , genTranslate
  , genRotate
  , genScale
  ]
  where
    genIdentity = pure ("identity", mempty)

    genTranslate = do
      x <- Gen.float (Range.linearFracFrom 0.0 (-1e6) 1e6)
      y <- Gen.float (Range.linearFracFrom 0.0 (-1e6) 1e6)
      z <- Gen.float (Range.linearFracFrom 0.0 (-1e6) 1e6)
      when (abs x == 0 && abs y == 0 && abs z == 0) Gen.discard
      pure
        ( printf "translate %0.4f %0.4f %0.4f" x y z
        , Transform.translate x y z
        )

    genRotate = do
      (name, axis) <- Gen.element
        [ ("rotate/x", Transform.rotateX)
        , ("rotate/y", Transform.rotateY)
        , ("rotate/z", Transform.rotateZ)
        ]
      angle <- genAngle
      when (abs angle == 0) Gen.discard
      pure
        ( printf "%s %0.4f" name angle
        , axis angle
        )

    genScale = do
      x <- Gen.float (Range.linearFracFrom 1.0 1e-6 1e6)
      y <- Gen.float (Range.linearFracFrom 1.0 1e-6 1e6)
      z <- Gen.float (Range.linearFracFrom 1.0 1e-6 1e6)
      when (abs x == 1 && abs y == 1 && abs z == 1) Gen.discard
      pure
        ( printf "scale %0.4f %0.4f %0.4f" x y z
        , Transform.scale3 x y z
        )