-- most functions and data types are from https://github.com/cobbpg/sloth2d module Graphics.Formats.Collada.Vector2D3D ( Angle , V2(..), V3(..), V4(..) , T2 , unit, (*.), dot, dot3, cross, cross3, perpL, perpR, mul, divide , turn, turnL, turnNL, turnR, turnNR, parv , square, mag, norm, dir, v_len, set_len , inverse, (<>) , translate, rotate, scale , translationOf, rotationOf, scaleOf , withTranslation, withRotation, withScale ) where import Data.Monoid infixl 7 `dot`, `cross` infixl 5 `turn`, `turnL`, `turnNL`, `turnR`, `turnNR`, `parv` -- | An angle is a number between -pi and pi. type Angle = Float -- | 2D vector: a pair of coordinates. data V2 = V {-# UNPACK #-} !Float {-# UNPACK #-} !Float deriving (Show, Eq, Ord) data V3 = V3 {-# UNPACK #-} !Float {-# UNPACK #-} !Float {-# UNPACK #-} !Float deriving (Show, Eq, Ord) data V4 = V4 {-# UNPACK #-} !Float {-# UNPACK #-} !Float {-# UNPACK #-} !Float {-# UNPACK #-} !Float deriving (Show, Eq, Ord) -- | 2D affine transformation. No shearing allowed, only translation, -- rotation, and scaling. Transformations can be chained with -- 'mappend', and 'mempty' is the identity transformation. data T2 = T {-# UNPACK #-} !Float {-# UNPACK #-} !Float {-# UNPACK #-} !Float {-# UNPACK #-} !Float deriving Show instance Num V2 where V x1 y1 + V x2 y2 = V (x1+x2) (y1+y2) V x1 y1 - V x2 y2 = V (x1-x2) (y1-y2) V x1 y1 * V x2 y2 = V (x1*x2) (y1*y2) negate (V x y) = V (negate x) (negate y) abs (V x y) = V (abs x) (abs y) signum (V x y) = V (signum x) (signum y) fromInteger n = let n' = fromInteger n in V n' n' instance Num V3 where V3 x1 y1 z1 + V3 x2 y2 z2 = V3 (x1+x2) (y1+y2) (z1+z2) V3 x1 y1 z1 - V3 x2 y2 z2 = V3 (x1-x2) (y1-y2) (z1-z2) V3 x1 y1 z1 * V3 x2 y2 z2 = V3 (x1*x2) (y1*y2) (z1*z2) negate (V3 x y z) = V3 (negate x) (negate y) (negate z) abs (V3 x y z) = V3 (abs x) (abs y) (abs z) signum (V3 x y z) = V3 (signum x) (signum y) (signum z) fromInteger n = let n' = fromInteger n in V3 n' n' n' -- | Unit vector with the given direction. unit :: Angle -> V2 unit a = V (cos a) (sin a) -- | Multiplication with a scalar. (*.) :: V2 -> Float -> V2 V x y *. m = V (x*m) (y*m) -- | Multiplication with a scalar. mul (V3 x y z) c = (V3 (x*c) (y*c) (z*c)) -- | Division by a scalar. divide (V3 x y z) c = (V3 (x/c) (y/c) (z/c)) -- | Dot product. dot :: V2 -> V2 -> Float V x1 y1 `dot` V x2 y2 = x1*x2+y1*y2 -- | Dot product. dot3 :: V3 -> V3 -> Float V3 x1 y1 z1 `dot3` V3 x2 y2 z2 = x1*x2 + y1*y2 + z1*z2 -- | Perp-dot product (2D cross product). cross :: V2 -> V2 -> Float V x1 y1 `cross` V x2 y2 = x1*y2-y1*x2 -- | 3D cross product. cross3 :: V3 -> V3 -> V3 V3 x1 y1 z1 `cross3` V3 x2 y2 z2 = V3 (y1*z2-z1*y2) (z1*x2-x1*z2) (x1*y2-y1*x2) -- | Vector rotated 90 degrees leftwards. perpL :: V2 -> V2 perpL (V x y) = V (-y) x -- | Vector rotated 90 degrees rightwards. perpR :: V2 -> V2 perpR (V x y) = V y (-x) -- | Relative direction of two vectors: @turn v1 v2@ equals @GT@ if -- @v2@ takes a left turn with respect to @v1@, @LT@ if it is a right -- turn, and @EQ@ if they are parallel. turn :: V2 -> V2 -> Ordering V x1 y1 `turn` V x2 y2 = compare (x1*y2) (y1*x2) -- | @turnL v1 v2 == (turn v1 v2 == GT)@ turnL :: V2 -> V2 -> Bool V x1 y1 `turnL` V x2 y2 = x1*y2 > y1*x2 -- | @turnNL v1 v2 == (turn v1 v2 /= GT)@ turnNL :: V2 -> V2 -> Bool V x1 y1 `turnNL` V x2 y2 = x1*y2 <= y1*x2 -- | @turnR v1 v2 == (turn v1 v2 == LT)@ turnR :: V2 -> V2 -> Bool V x1 y1 `turnR` V x2 y2 = x1*y2 < y1*x2 -- | @turnNR v1 v2 == (turn v1 v2 /= LT)@ turnNR :: V2 -> V2 -> Bool V x1 y1 `turnNR` V x2 y2 = x1*y2 >= y1*x2 -- | @parv v1 v2 == (turn v1 v2 == EQ)@ parv :: V2 -> V2 -> Bool V x1 y1 `parv` V x2 y2 = x1*y2 == y1*x2 -- | Vector length squared. square :: V2 -> Float square v = v `dot` v -- | 3d Vector length squared. square3 :: V3 -> Float square3 v = v `dot3` v -- | Vector length. mag :: V2 -> Float mag = sqrt . square -- | 3d Vector length. v_len = sqrt . square3 -- | Set Vector length. set_len (V3 x y z) l = (V3 (x*c*l) (y*c*l) (z*c*l)) where c = 1 / v_len (V3 x y z) -- | The angle of a vector with respect to the X axis. dir :: V2 -> Angle dir (V x y) = atan2 y x -- | Vector normalisation. norm :: V2 -> V2 norm v@(V x y) = V (x*m) (y*m) where m = recip (mag v) instance Semigroup T2 where T rx1 ry1 tx1 ty1 <> T rx2 ry2 tx2 ty2 = T rx ry tx ty where rx = rx1*rx2-ry1*ry2 ry = ry1*rx2+rx1*ry2 tx = rx1*tx2-ry1*ty2+tx1 ty = ry1*tx2+rx1*ty2+ty1 instance Monoid T2 where mempty = scale 1 -- | Inverse transformation inverse :: T2 -> T2 inverse (T rx ry tx ty) = T (rx*m) (-ry*m) tx' ty' where m = recip (rx*rx+ry*ry) tx' = m*(-ry*ty-rx*tx) ty' = m*(ry*tx-rx*ty) -- | Transformation applied to a vector. (<.>) :: T2 -> V2 -> V2 T rx ry tx ty <.> V x y = V x' y' where x' = rx*x-ry*y+tx y' = ry*x+rx*y+ty -- | Transformation representing a translation. translate :: V2 -> T2 translate (V x y) = T 1 0 x y -- | Transformation representing a rotation. rotate :: Angle -> T2 rotate a = T (cos a) (sin a) 0 0 -- | Transformation representing a scaling. scale :: Float -> T2 scale m = T m 0 0 0 -- | The translation factor of a transformation. translationOf :: T2 -> V2 translationOf (T _ _ tx ty) = V tx ty -- | The rotation factor of a transformation. rotationOf :: T2 -> Angle rotationOf (T rx ry _ _) = dir (V rx ry) -- | The scaling factor of a transformation. scaleOf :: T2 -> Float scaleOf (T rx ry _ _) = mag (V rx ry) -- | Replacing the translation factor of a transformation. withTranslation :: T2 -> V2 -> T2 T rx ry _ _ `withTranslation` V x y = T rx ry x y -- | Replacing the rotation factor of a transformation. withRotation :: T2 -> Angle -> T2 T rx ry tx ty `withRotation` a = T rx' ry' tx ty where V rx' ry' = unit a *. mag (V rx ry) -- | Replacing the scaling factor of a transformation. withScale :: T2 -> Float -> T2 T rx ry tx ty `withScale` m = T (m'*rx) (m'*ry) tx ty where m' = m / mag (V rx ry)