{-# LANGUAGE BangPatterns #-} module Geometry.Vector where import Geometry.Data {- | Moves from to three dimensions, adding zero in z direction. -} zeroZ :: Point2 -> Point3 {-# INLINE zeroZ #-} zeroZ (x,y) = (x,y,0) infixl 6 +.+, -.- infixl 7 *.* {- | 2D coordinate-wise addition. -} (+.+) :: Point2 -> Point2 -> Point2 {-# INLINE (+.+) #-} (x1, y1) +.+ (x2, y2) = let !x = x1 + x2 !y = y1 + y2 in (x, y) {- | 2D coordinate-wise subtraction. -} (-.-) :: Point2 -> Point2 -> Point2 {-# INLINE (-.-) #-} (x1, y1) -.- (x2, y2) = let !x = x1 - x2 !y = y1 - y2 in (x, y) {- | 2D scalar multiplication. -} (*.*) :: Float -> Point2 -> Point2 {-# INLINE (*.*) #-} a *.* (x2, y2) = let !x = a * x2 !y = a * y2 in (x, y) infixl 6 +.+.+, -.-.- infixl 7 *.*.* {- | 3D coordinate-wise addition. -} (+.+.+) :: Point3 -> Point3 -> Point3 {-# INLINE (+.+.+) #-} (x1, y1, z1) +.+.+ (x2, y2, z2) = let !x = x1 + x2 !y = y1 + y2 !z = z1 + z2 in (x, y, z) {- | 3D coordinate-wise subtraction. -} (-.-.-) :: Point3 -> Point3 -> Point3 {-# INLINE (-.-.-) #-} (x1, y1, z1) -.-.- (x2, y2, z2) = let !x = x1 - x2 !y = y1 - y2 !z = z1 - z2 in (x, y, z) {- | 3D scalar multiplication. -} (*.*.*) :: Point3 -> Point3 -> Point3 {-# INLINE (*.*.*) #-} (x1, y1, z1) *.*.* (x2, y2, z2) = let !x = x1 * x2 !y = y1 * y2 !z = z1 * z2 in (x, y, z) {- | Normalize a vector to length 1. -} normalizeV :: Point2 -> Point2 {-# INLINE normalizeV #-} normalizeV p = (1 / magV p) *.* p {- | Angle between two vectors. Always positive. -} angleVV :: Point2 -> Point2 -> Float {-# INLINE angleVV #-} angleVV a b | a == b = 0 | otherwise = let ma = magV a mb = magV b d = a `dotV` b in acos $ d / (ma * mb) {- | Safe version of 'angleVV' that returns 0 if either vector is null. -} safeAngleVV :: Point2 -> Point2 -> Float {-# INLINE safeAngleVV #-} safeAngleVV a b | a == (0,0) || b == (0,0) = 0 | otherwise = angleVV a b {- | Dot product. -} dotV :: Point2 -> Point2 -> Float {-# INLINE dotV #-} dotV (x,y) (z,w) = x*z + y*w {- | Given vector, returns the angle, anticlockwise from +ve x-axis, in radians. -} argV :: Point2 -> Float {-# INLINE argV #-} argV (x,y) = normalizeAngle $ atan2 y x {- | Determinant of the matrix formed by two vectors. -} detV :: Point2 -> Point2 -> Float {-# INLINE detV #-} detV (x1, y1) (x2, y2) = x1 * y2 - y1 * x2 {- | Given an angle in radians, anticlockwise from +ve x-axis, - returns the corresponding unit vector. -} unitVectorAtAngle :: Float -> Point2 {-# INLINE unitVectorAtAngle #-} unitVectorAtAngle r = (cos r, sin r) -- | Rotate a vector by an angle (in radians). +ve angle is counter-clockwise. rotateV :: Float -> Point2 -> Point2 rotateV r (x, y) = ( x * cos r - y * sin r , x * sin r + y * cos r ) {-# INLINE rotateV #-} -- | Convert degrees to radians degToRad :: Float -> Float degToRad d = d * pi / 180 {-# INLINE degToRad #-} -- | Convert radians to degrees radToDeg :: Float -> Float radToDeg r = r * 180 / pi {-# INLINE radToDeg #-} -- | Normalize an angle to be between 0 and 2*pi radians normalizeAngle :: Float -> Float {-# INLINE normalizeAngle #-} normalizeAngle f = f - 2 * pi * floor' (f / (2 * pi)) where floor' :: Float -> Float floor' x = fromIntegral (floor x :: Int) {- | Rotate vector by pi/2 clockwise. -} vNormal :: Point2 -> Point2 {-# INLINE vNormal #-} vNormal (x,y) = (y,-x) {- | Negate a vector. -} vInverse :: Point2 -> Point2 vInverse (x,y) = (-x,-y) {- | Normalize a vector safely: on (0,0) return (0,0). -} safeNormalizeV :: Point2 -> Point2 {-# INLINE safeNormalizeV #-} safeNormalizeV (0,0) = (0,0) safeNormalizeV p = (1/magV p ) *.* p {- | Magnitude of a vector. -} magV :: Point2 -> Float {-# INLINE magV #-} magV (x,y) = sqrt $ x^(2::Int) + y^(2::Int) {- | Magnitude of the cross product of two vectors. Identical to detV. -} crossV :: Point2 -> Point2 -> Float crossV (ax,ay) (bx,by) = ax*by - ay*bx {- | TO CHECK Orthographic projection of one vector onto another. -} projV :: Point2 -> Point2 -> Point2 projV fromv onv = (fromv `dotV` onv) / (onv `dotV` onv) *.* onv