-- | Module providing basic helper functions to help -- build vector/point calculations. module Graphics.Rasterific.Operators ( Point -- * Lifted operators , (^&&^) , (^||^) , (^==^) , (^/=^) , (^<=^) , (^<^) , (^<) -- * Lifted functions , vmin , vmax , vabs , vfloor , vceil , clampPoint , midPoint , middle , vpartition , normal , ifZero , isNearby , isDistingableFrom , isDegenerate ) where import Control.Applicative( liftA2, liftA3 ) import Graphics.Rasterific.Linear ( V2( .. ) , Additive( .. ) , Epsilon( nearZero ) , (^+^) , (^*) , dot , normalize ) infix 4 ^<, ^<=^, ^<^, ^==^, ^/=^ infixr 3 ^&&^ infixr 2 ^||^ -- | Represent a point type Point = V2 Float -- | Pairwise boolean and operator (^&&^) :: (Applicative a) => a Bool -> a Bool -> a Bool {-# INLINE (^&&^) #-} (^&&^) = liftA2 (&&) -- | Pairwise boolean or operator (^||^) :: (Applicative a) => a Bool -> a Bool -> a Bool {-# INLINE (^||^) #-} (^||^) = liftA2 (||) -- | Pairwise vector/point equal operator (^==^) :: (Eq v, Applicative a) => a v -> a v -> a Bool {-# INLINE (^==^) #-} (^==^) = liftA2 (==) -- | Pairwise vector/point lower than or equal operator (^<=^) :: (Ord v, Applicative a) => a v -> a v -> a Bool {-# INLINE (^<=^) #-} (^<=^) = liftA2 (<=) -- | Pairwise vector/point lower than operator (^<^) :: (Ord v, Applicative a) => a v -> a v -> a Bool {-# INLINE (^<^) #-} (^<^) = liftA2 (<) -- | Component/scalar lower than operator. (^<) :: (Applicative a, Ord v) => a v -> v -> a Bool {-# INLINE (^<) #-} (^<) vec v = (< v) <$> vec -- | Pairwise vector/point difference operator. (^/=^) :: (Applicative a, Eq v) => a v -> a v -> a Bool {-# INLINE (^/=^) #-} (^/=^) = liftA2 (/=) -- | Min function between two vector/points. -- Work on every component separately. vmin :: (Ord n, Applicative a) => a n -> a n -> a n {-# INLINE vmin #-} vmin = liftA2 min -- | Max function between to vector/point. -- Work on every component separatly. vmax :: (Ord n, Applicative a) => a n -> a n -> a n {-# INLINE vmax #-} vmax = liftA2 max -- | Abs function for every component of the vector/point. vabs :: (Num n, Functor a) => a n -> a n {-# INLINE vabs #-} vabs = fmap abs -- | Floor function for every component of the vector/point. vfloor :: (Functor a) => a Float -> a Int {-# INLINE vfloor #-} vfloor = fmap floor -- | ceil function for every component of the vector/point. vceil :: (Functor a) => a Float -> a Int {-# INLINE vceil #-} vceil = fmap ceiling -- | Given a point, clamp every coordinates between -- a given minimum and maximum. clampPoint :: Point -> Point -> Point -> Point {-# INLINE clampPoint #-} clampPoint mini maxi v = vmin maxi $ vmax mini v -- | Given two points, return a point in the middle -- of them. midPoint :: (Additive a, Fractional coord) => a coord -> a coord -> a coord {-# INLINE midPoint #-} midPoint a b = (a ^+^ b) ^* 0.5 middle :: (Fractional a) => a -> a -> a {-# INLINE middle #-} middle a b = (a + b) * 0.5 -- | Given a boolean choice vector, return elements of -- the first one if true, of the second one otherwise. vpartition :: (Applicative a) => a Bool -> a v -> a v -> a v {-# INLINE vpartition #-} vpartition = liftA3 choose where choose True a _ = a choose False _ b = b -- | Calculate a normal vector normal :: (Floating v, Epsilon v) => V2 v -> V2 v -> V2 v {-# INLINE normal #-} normal (V2 ax ay) (V2 bx by) = normalize $ V2 (ay - by) (bx - ax) -- | Return the second operand if the vector is -- nearly null ifZero :: (Epsilon v) => v -> v -> v {-# INLINE ifZero #-} ifZero u v | nearZero u = v | otherwise = u -- | Tell if two points are nearly indistinguishable. -- If indistinguishable, we can treat them as the same -- point. -- point with degenerate coordinates (Infinity/NaN) will be considered -- as nearby. isNearby :: Point -> Point -> Bool {-# INLINE isNearby #-} isNearby p1 p2 = -- we keep really small distances because when drawing geometry -- (possibly scaled) from a large model, every small line account -- to the coverage, and discarding "small" lines will make artifact -- because we didn't count coverage correctly. squareDist < 0.0001 || isNaN squareDist || isInfinite squareDist -- degenerate case protection where vec = p1 ^-^ p2 squareDist = vec `dot` vec isDegenerate :: Point -> Bool isDegenerate (V2 x y) = isNaN x || isNaN y || isInfinite x || isInfinite y -- | simply `not (a `isNearby` b)` isDistingableFrom :: Point -> Point -> Bool {-# INLINE isDistingableFrom #-} isDistingableFrom a b = not $ isNearby a b