-- | -- Module : Cartesian.Space -- Copyright : (C) 2015 Jonatan H Sundqvist -- License : MIT-style (see the file LICENSE) -- Maintainer : Jonatan H Sundqvist -- Stability : provisional -- Portability : Portable -- -- Vector and coordinate system utilities. -- -- Cartesian.hs -- This module exports the API for the Cartesian project -- -- Jonatan H Sundqvist -- January 27 2015 -- -- TODO | - Haddock header, sections, full coverage -- - Separate 2D and 3D modules (✓) -- - Factor out common functionality for Space.hs and Plane.hs -- SPEC | - -- - module Cartesian.Space where -------------------------------------------------------------------------------------------------------------------------------------------- -- We'll need these -------------------------------------------------------------------------------------------------------------------------------------------- import Data.List (sort, minimumBy) import Data.Ord (comparing) -------------------------------------------------------------------------------------------------------------------------------------------- -- Types -------------------------------------------------------------------------------------------------------------------------------------------- -- | data Vector num = Vector num num num -- TODO: Constraints on argument types (cf. GADT) (?) -- | data Line num = Line (Vector num) (Vector num) -- | Why the hell did I write this useless function? vector :: Num a => a -> a -> a -> Vector a vector = Vector -- SublimeHaskell prefers the eta-reduced version (point-free) -------------------------------------------------------------------------------------------------------------------------------------------- -- Instances -------------------------------------------------------------------------------------------------------------------------------------------- -- | instance (Floating a, Eq a) => Num (Vector a) where -- TODO: Helper method to reduce boilerplate for component-wise operations (+) = dotwise (+) (-) = dotwise (-) (*) = dotwise (*) -- TODO: Is this really correct? fromInteger n = Vector (fromInteger n) 0 0 signum v@(Vector x y z) = Vector (x/mag v) (y/mag v) (z/mag v) -- TODO: Proper way of implementing this function for vectors abs v = Vector (mag v) (0) (0) -------------------------------------------------------------------------------------------------------------------------------------------- -- Functions -------------------------------------------------------------------------------------------------------------------------------------------- -- Vector math ----------------------------------------------------------------------------------------------------------------------------- -- | Performs component-wise operations dotwise :: (a -> b -> c) -> Vector a -> Vector b -> Vector c dotwise f (Vector x y z) (Vector x' y' z') = Vector (f x x') (f y y') (f z z') -- | Dot product of two vectors dot :: Floating a => Vector a -> Vector a -> a dot (Vector x y z) (Vector x' y' z') = (x * x') + (y * y') + (z * z') -- TODO: Refactor with Num instance (?) -- | Euclidean distance between two points euclidean :: Floating a => Vector a -> Vector a -> a euclidean a b = sqrt $ dot a b -- | magnitude :: (Floating a, Eq a) => Vector a -> a magnitude v = euclidean v v mag :: (Floating a, Eq a) => Vector a -> a mag = magnitude -- | Angle (in radians) between the positive X-axis and the vector -- argument :: (Floating a, Eq a) => Vector a -> a -- argument (Vector 0 0 0) = 0 -- argument (Vector x y z) = atan $ y/x -- arg :: (Floating a, Eq a) => Vector a -> a -- arg = argument -- | Vector -> (magnitude, argument) -- polar :: (Floating a, Eq a) => Vector a -> (a, a) -- polar v@(Vector x y) = (magnitude v, argument v) -------------------------------------------------------------------------------------------------------------------------------------------- -- | Intersect -- TODO: Math notes, MathJax or LaTex -- TODO: Intersect for curves (functions) and single points (?) -- TODO: Polymorphic, typeclass (lines, shapes, ranges, etc.) (?) intersect :: Num a => Line a -> Line a -> Maybe (Vector a) intersect _ _ = error "Not implemented" -- Nothing -- | intersects :: Num a => Line a -> Line a -> Bool intersects a b = case intersect a b of Just _ -> True Nothing -> False -- | Yields the overlap of two closed intervals (n ∈ R) -- TODO: Normalise intervals (eg. (12, 5) -> (5, 12)) overlap :: Real a => (a, a) -> (a, a) -> Maybe (a, a) overlap a b | leftmost /= (α, β) = Just $ (β, γ) -- | otherwise = Nothing -- where [α, β, γ, _] = sort [fst a, snd a, fst b, snd b] -- That's right. leftmost = minimumBy (comparing fst) [a, b] -- -- | -- TODO: Intersect Rectangles -- | Coefficients for the linear function of a Line (slope, intercept). The Z-component is ignored. -- Fails for vertical and horizontal lines. -- -- TODO: Use Maybe (?) -- coefficients :: (Fractional a, Eq a) => Line a -> Maybe (a, a) coefficients (Line (Vector ax ay _) (Vector bx by _)) | ax == bx = Nothing | ay == ay = Nothing | otherwise = let slope = (by - ay)/(bx - ax) in Just (slope, ay - slope*ax)