packages feed

geom2d-0.1.3.1: Test/Point.hs

{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE ScopedTypeVariables #-}

import Data.AEq
import Data.Maybe
import Geom2d.Point
import Geom2d.Rotation
import Test.QuickCheck hiding (scale)
import Test.Utils
import Data.Fixed

mkpropPoint :: forall a p. (Arbitrary (p a), Point p, Eq a) =>
                 p a -> Bool
mkpropPoint p =
  and [ prop_identity
      ]
  where prop_identity = x p == x idPoint &&
                        y p == y idPoint
        idPoint :: p a
        idPoint = fromCoords (x p) (y p)

mkpropNum :: (Arbitrary a, Num a) => (a -> a -> Bool) -> a -> Bool
mkpropNum isEqual x =
    x `isEqual` (signum x * abs x)

mkpropFunctor :: (Arbitrary (f a), Eq (f a), Functor f) =>
                  f a -> Bool
mkpropFunctor x =
  fmap id x == x

mkpropScale :: (Arbitrary (p a), Scale p, Floating a, Point p) =>
                (a -> a -> Bool) -> p a -> a -> Bool
mkpropScale comparison p a =
  (magnitude p * abs a) `comparison`
  magnitude (a `scale` p)

mkpropNormalize :: (Arbitrary (p a), Point p, Scale p, Floating a, AEq a
                    , Num a) =>
                p a -> Bool
mkpropNormalize v =
  maybe (magnitude v ~== 0)
  ( (~== 1).magnitude )
  ( normalize v)

prop_scaleTo :: Point' Float -> Float -> Bool
prop_scaleTo vector x =
  fromMaybe (magnitude vector == 0) $ do
    scaledVector <- scaleTo x vector
    return (magnitude scaledVector ~== abs x)

prop_point_point' :: Point' Integer -> Bool
prop_point_point' = mkpropPoint

prop_num_point' :: Point' Float -> Bool
prop_num_point' = mkpropNum (~==)

prop_functor_point' :: Point' Integer -> Bool
prop_functor_point' = mkpropFunctor

prop_point'_magnitude :: Point' Float -> Bool
prop_point'_magnitude p =
  sqrt ( x p ^ 2 + y p ^ 2 ) ~== magnitude p

prop_triarea :: Float -> Float -> Bool
prop_triarea a b =
  triArea triangle ~== abs ((a * b) / 2)
  where triangle :: Triangle (Point' Float)
        triangle = ( fromCoords 0 0
                   , fromCoords a 0
                   , fromCoords 0 b
                   )

prop_point_scale :: Point' Float -> Float -> Bool
prop_point_scale = mkpropScale (~==)

prop_point_normalize :: Point' Float -> Bool
prop_point_normalize = mkpropNormalize

prop_pointInTriangle :: Bool
prop_pointInTriangle =
  pointInTriangle tri p
  where tri = ( fromCoords (-1) (-1)
              , fromCoords 1 (-1)
              , fromCoords 0 1
              )
        p :: Point' Float
        p = fromCoords 0 0

prop_pointInTriangle_onVert :: Bool
prop_pointInTriangle_onVert =
  pointInTriangle tri p
  where p = fromCoords 0 0
        tri :: Triangle (Point' Float)
        tri = (p, fromCoords 1 1, fromCoords 1 (-1))

prop_angle_zero :: Bool
prop_angle_zero =
  angle (fromCoords 1 0 :: Point' Float) == Just 0

prop_point_show_read :: Point' Float -> Bool
prop_point_show_read p =
  p == (read.show) p

prop_point_add :: Point' Float -> Point' Float -> Bool
prop_point_add p q =
  x (p + q) == x p + x q &&
  y (p + q) == y p + y q

prop_point_negate :: Point' Float -> Bool
prop_point_negate p =
  x (negate p) == negate (x p) &&
  y (negate p) == negate (y p)

prop_triangle_invalid :: Bool
prop_triangle_invalid =
  let tri :: Triangle (Point' Float)
      tri = ( fromCoords 0 0
            , fromCoords 1 1
            , fromCoords 1 1
            )
  in not (pointInTriangle tri (fromCoords 0 0))

prop_rotation_point_linear :: Point' Float -> Bool
prop_rotation_point_linear x =
  fromMaybe True
  ( (~==) <$>
    angle (r `rotate` x) <*>
    ((subtract pi).((`mod'` (2*pi)).(+pi).(+r)) <$> angle x)
  )
  where r = 1.2

prop_rotation_point_bounds :: Point' Float -> Bool
prop_rotation_point_bounds x =
  fromMaybe True
  ( fmap
    (\a -> a >= (- pi) && a <= pi)
    (angle x)
  )

return []
runTests = $quickCheckAll

main :: IO ()
main = do
  putStrLn "Test Point"
  runTests >>= doExit