Cartesian (empty) → 0.1.0.0
raw patch · 11 files changed
+862/−0 lines, 11 filesdep +basedep +lenssetup-changed
Dependencies added: base, lens
Files
- Cartesian.cabal +68/−0
- LICENSE.md +21/−0
- README.md +4/−0
- Setup.hs +2/−0
- src/Cartesian/Lenses.hs +59/−0
- src/Cartesian/Plane.hs +212/−0
- src/Cartesian/Plane/BoundingBox.hs +85/−0
- src/Cartesian/Plane/BoundingBox/Lenses.hs +128/−0
- src/Cartesian/Plane/Types.hs +55/−0
- src/Cartesian/Plane/Utilities.hs +71/−0
- src/Cartesian/Space.hs +157/−0
+ Cartesian.cabal view
@@ -0,0 +1,68 @@+-- Initial Cartesian.cabal generated by cabal init. For further +-- documentation, see http://haskell.org/cabal/users-guide/ + +-- The name of the package. +name: Cartesian + +-- The package version. See the Haskell package versioning policy (PVP) +-- for standards guiding when and how versions should be incremented. +-- http://www.haskell.org/haskellwiki/Package_versioning_policy +-- PVP summary: +-+------- breaking API changes +-- | | +----- non-breaking API additions +-- | | | +--- code changes with no API change +version: 0.1.0.0 + +-- A short (one-line) description of the package. +synopsis: Coordinate systems + +-- A longer description of the package. +-- description: + +-- The license under which the package is released. +license: MIT + +-- The file containing the license text. +license-file: LICENSE.md + +-- The package author(s). +author: Jonatan H Sundqvist + +-- An email address to which users can send suggestions, bug reports, and +-- patches. +maintainer: jonatanhsundqvist@gmail.com + +-- A copyright notice. +-- copyright: + +category: Math + +build-type: Simple + +-- Extra files to be distributed with the package, such as examples or a +-- README. +extra-source-files: README.md + +-- Constraint on the version of Cabal needed to build this package. +cabal-version: >=1.10 + + +library + -- Modules exported by the library. + exposed-modules: Cartesian.Lenses, Cartesian.Plane, Cartesian.Space, + Cartesian.Plane.Utilities, Cartesian.Plane.Types, Cartesian.Plane.BoundingBox, Cartesian.Plane.BoundingBox.Lenses + + -- Modules included in this library but not exported. + -- other-modules: + + -- LANGUAGE extensions used by modules in this package. + other-extensions: TemplateHaskell, RankNTypes + + -- Other library packages from which modules are imported. + build-depends: base <= 4.8.1.0, + lens <= 4.13.0.0 + + -- Directories containing source files. + hs-source-dirs: src + + -- Base language which the package is written in. + default-language: Haskell2010
+ LICENSE.md view
@@ -0,0 +1,21 @@+The MIT License (MIT) + +Copyright (c) 2015 Jonatan H Sundqvist + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE.
+ README.md view
@@ -0,0 +1,4 @@+Cartesian +========= + +Functions and types for working with three-dimensional coordinate systems
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple +main = defaultMain
+ src/Cartesian/Lenses.hs view
@@ -0,0 +1,59 @@+-- | +-- Module : Cartesian.Lenses +-- Description : +-- Copyright : (c) Jonatan H Sundqvist, 2015 +-- License : MIT +-- Maintainer : Jonatan H Sundqvist +-- Stability : experimental|stable +-- Portability : POSIX (not sure) +-- + +-- Created August 30 2015 + +-- TODO | - +-- - + +-- SPEC | - +-- - + + + +-------------------------------------------------------------------------------------------------------------------------------------------- +-- GHC Pragmas +-------------------------------------------------------------------------------------------------------------------------------------------- +{-# LANGUAGE TemplateHaskell #-} + + + +-------------------------------------------------------------------------------------------------------------------------------------------- +-- API +-------------------------------------------------------------------------------------------------------------------------------------------- +module Cartesian.Lenses where + + + +-------------------------------------------------------------------------------------------------------------------------------------------- +-- We'll need these +-------------------------------------------------------------------------------------------------------------------------------------------- +import qualified Control.Lens as L + +import qualified Cartesian.Plane.Types as Plane +-- import qualified Cartesian.Space.Types as Space + +-- import qualified Cartesian.Plane as Plane +-- import qualified Cartesian.Space as Space + + + +-------------------------------------------------------------------------------------------------------------------------------------------- +-- Invoke the Templates! +-------------------------------------------------------------------------------------------------------------------------------------------- +-------------------------------------------------------------------------------------------------------------------------------------------- +L.makeLenses ''Plane.BoundingBox + + + +-------------------------------------------------------------------------------------------------------------------------------------------- + + +--------------------------------------------------------------------------------------------------------------------------------------------
+ src/Cartesian/Plane.hs view
@@ -0,0 +1,212 @@+-- | +-- Module : Cartesian.Plane +-- Description : +-- Copyright : (c) Jonatan H Sundqvist, year +-- License : MIT +-- Maintainer : Jonatan H Sundqvist +-- Stability : experimental|stable +-- Portability : POSIX (not sure) +-- + +-- Created date year + +-- TODO | - Which constraints are appropriate (Num is probably too generic, should be Real, maybe RealFrac) +-- - Strictness, performance + +-- SPEC | - +-- - + + + +-------------------------------------------------------------------------------------------------------------------------------------------- +-- API +-------------------------------------------------------------------------------------------------------------------------------------------- +module Cartesian.Plane where + + + +-------------------------------------------------------------------------------------------------------------------------------------------- +-- We'll need these +-------------------------------------------------------------------------------------------------------------------------------------------- +import Data.List (sort, minimumBy) +import Data.Ord (comparing) +import Data.Complex hiding (magnitude) + +import qualified Control.Lens as L + +-- import Southpaw.Utilities.Utilities (pairwise) + + + +-------------------------------------------------------------------------------------------------------------------------------------------- +-- Types +-------------------------------------------------------------------------------------------------------------------------------------------- +-------------------------------------------------------------------------------------------------------------------------------------------- +-- | +-- TODO: Rename (?) +data Vector num = Vector num num deriving (Eq, Show) -- TODO: Constraints on argument types (cf. GADT) (?) + + +-- | +data Line num = Line (Vector num) (Vector num) + + +-- | +-- TODO: Rename (eg. 'Shape') (?) +type Polygon num = [Vector num] + + +-- | +data Linear num = Linear { intercept :: num, slope :: num } + + +-------------------------------------------------------------------------------------------------------------------------------------------- +-- | +-- type Domain + + +-- | Determines if a point lies within a polygon using the odd-even method. +-- +-- TODO: Use epsilon (?) +-- TODO: How to treat points that lie on an edge +inside :: Num n => Polygon n -> Vector n -> Bool +inside (p:olygon) (Vector x y) = undefined + where + lines = (p:olygon)++[p] -- Close the loop + -- between (Line (Vector ax ay) (Vector bx by)) = _ + + + +-------------------------------------------------------------------------------------------------------------------------------------------- +-- Instances +-------------------------------------------------------------------------------------------------------------------------------------------- +-- | abs v * signum v == v +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 + signum (Vector 0 0) = Vector 0 0 + signum v@(Vector x y) = Vector (x/mag v) (y/mag v) + abs a = Vector (euclidean a a) 0 + + + +-------------------------------------------------------------------------------------------------------------------------------------------- +-- Functions +-------------------------------------------------------------------------------------------------------------------------------------------- +-- Vector math ------------------------------------------------------------------------------------ +-- | Performs component-wise operations +dotwise :: (a -> b -> c) -> Vector a -> Vector b -> Vector c +dotwise f (Vector x y) (Vector x' y') = Vector (f x x') (f y y') + + +-- | Dot product of two vectors +dot :: Floating a => Vector a -> Vector a -> a +dot (Vector x y) (Vector x' y') = (x * x') + (y * y') -- 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 +argument (Vector x y) = 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) + + +-- Geometry --------------------------------------------------------------------------------------- +-- | Yields the point at which two finite lines intersect. The lines are defined inclusively by +-- their endpoints. The result is wrapped in a Maybe value to account for non-intersecting +-- lines. +-- +-- TODO: Move equation solving to separate function (two linear functions) +-- TODO: Simplify logic by considering f(x) = y for vertical lines (?) +-- TODO: Return Either instead of Maybe (eg. Left "parallel") (?) +-- +-- TODO: Math notes, MathJax or LaTex +-- TODO: Intersect for curves (functions) and single points (?) +-- TODO: Polymorphic, typeclass (lines, shapes, ranges, etc.) (?) +-- +intersect :: RealFrac n => Line n -> Line n -> Maybe (Vector n) +intersect a b + | (fst $ deltas a) == 0 = Just $ error "Not implemented" + | (fst $ deltas b) == 0 = Just $ error "Not implemented" + | slope a == slope b = Nothing + | otherwise = Nothing + where deltas (Line (Vector ax ay) (Vector bx by)) = (bx - ax, by - ay) -- TODO: Rename (eg. deltas) (?) + vertical (Line (Vector ax _) (Vector bx _)) = ax == bx + slope line = let (dx, dy) = deltas line in dy/dx + intercept line@(Line (Vector x y) _) + | vertical line = Nothing + | otherwise = Just $ y - slope line * x + + +-- Geometry --------------------------------------------------------------------------------------- +-- | +-- inside :: (Num n, Ord n) => Triangle n -> Point n -> Bool +-- inside _ _ = False + + +-- | +intersects :: RealFrac r => Line r -> Line r -> 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). +-- Fails for vertical and horizontal lines. +-- +-- TODO: Use Maybe (?) +-- TODO: Rename (eg. toLinear, function) (?) +-- +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) + + +-- Linear functions ------------------------------------------------------------------------------- +-- | Solves a linear equation for x (f(x) = g(x)) +-- TODO: Use Epsilon (?) +solve :: (Fractional n, Eq n) => Linear n -> Linear n -> Maybe n +solve f g + | slope f == slope g = Nothing + | otherwise = Just $ (intercept f - intercept g)/(slope f - slope g)
+ src/Cartesian/Plane/BoundingBox.hs view
@@ -0,0 +1,85 @@+-- | +-- Module : Cartesian.Plane.BoundingBox +-- Description : +-- Copyright : (c) Jonatan H Sundqvist, 2015 +-- License : MIT +-- Maintainer : Jonatan H Sundqvist +-- Stability : experimental|stable +-- Portability : POSIX (not sure) +-- + +-- Created September 7 2015 + +-- TODO | - Corner lenses +-- - + +-- SPEC | - +-- - + + + +-------------------------------------------------------------------------------------------------------------------------------------------- +-- GHC Pragmas +-------------------------------------------------------------------------------------------------------------------------------------------- + + + +-------------------------------------------------------------------------------------------------------------------------------------------- +-- API +-------------------------------------------------------------------------------------------------------------------------------------------- +module Cartesian.Plane.BoundingBox (module Cartesian.Plane.Types, + module Cartesian.Plane.BoundingBox, + module Cartesian.Plane.BoundingBox.Lenses) where + + + +-------------------------------------------------------------------------------------------------------------------------------------------- +-- We'll need these +-------------------------------------------------------------------------------------------------------------------------------------------- +import Data.Complex +import Data.Functor ((<$>)) +import Data.List (sort) + +import Control.Lens + +import Cartesian.Plane.Types +import Cartesian.Plane.BoundingBox.Lenses +import Cartesian.Plane.Utilities + + + +-------------------------------------------------------------------------------------------------------------------------------------------- +-- Functions +-------------------------------------------------------------------------------------------------------------------------------------------- + +-- Convenience constructors ---------------------------------------------------------------------------------------------------------------- + +-- | +-- TODO: Better name (?) +-- TODO: Don't make assumptions about WHICH corners they are (✓) +fromCorners :: RealFloat f => Complex f -> Complex f -> BoundingBox f +fromCorners nw@(n:+w) se@(s:+e) = let size = dotmap abs (se-nw) in BoundingBox { _centre=dotwise min nw se+size*(0.5:+0.0), _size=size } + + +-- | Creates a bounding box from a topleft and size vector. +fromCornerAndSize :: RealFloat f => Complex f -> Complex f -> BoundingBox f +fromCornerAndSize nw size' = BoundingBox { _centre=nw+size'*0.5, _size=size' } + + +-- | Top Left Bottom Right +fromSides :: RealFloat f => f -> f -> f -> f -> BoundingBox f +fromSides top left bottom right = fromCorners (left:+top) (right:+bottom) + +-- Booleans -------------------------------------------------------------------------------------------------------------------------------- + +-- | +intersect :: (RealFloat f, Ord f) => BoundingBox f -> BoundingBox f -> Maybe (BoundingBox f) +intersect a b = do + (left', right') <- overlap (a^.left, a^.right) (b^.left, b^.right) + (top', bottom') <- overlap (a^.top, a^.bottom) (b^.top, b^.bottom) + return $ fromSides top' left' bottom' right' + where + overlap (a, b) (c, d) + | min (a, b) (c, d) == (a', b') = Just (b', c') + | otherwise = Nothing + where [a', b', c', d'] = sort [a, b, c, d]
+ src/Cartesian/Plane/BoundingBox/Lenses.hs view
@@ -0,0 +1,128 @@+-- | +-- Module : Cartesian.BoundingBox.Lenses +-- Description : +-- Copyright : (c) Jonatan H Sundqvist, 2015 +-- License : MIT +-- Maintainer : Jonatan H Sundqvist +-- Stability : experimental|stable +-- Portability : POSIX (not sure) +-- + +-- Created October 21 2015 + +-- TODO | - +-- - + +-- SPEC | - +-- - + + + +-------------------------------------------------------------------------------------------------------------------------------------------- +-- GHC Pragmas +-------------------------------------------------------------------------------------------------------------------------------------------- +{-# LANGUAGE RankNTypes #-} + + + +-------------------------------------------------------------------------------------------------------------------------------------------- +-- API +-------------------------------------------------------------------------------------------------------------------------------------------- +module Cartesian.Plane.BoundingBox.Lenses where + + + +-------------------------------------------------------------------------------------------------------------------------------------------- +-- We'll need these +-------------------------------------------------------------------------------------------------------------------------------------------- +import Data.Complex (Complex ((:+))) +import Data.Functor ((<$>)) +import Control.Lens + +import Cartesian.Plane.Types + + + +-------------------------------------------------------------------------------------------------------------------------------------------- +-- Lenses +-------------------------------------------------------------------------------------------------------------------------------------------- + + +-- | +-- TODO: Make sure invariants remain true (eg. left < right) +-- TODO: Make coordinate-system independent (eg. direction of axes) +makeBoundingBoxSideLens :: RealFloat f => (BoundingBox f -> f) -> (BoundingBox f -> f -> (f, f, f, f)) -> Lens (BoundingBox f) (BoundingBox f) f f +makeBoundingBoxSideLens oldside newsides f s@(BoundingBox { _centre=(cx:+cy), _size=(dx:+dy) }) = assemble <$> f (oldside s) + where + assemble newside = let (nleft, nright, ntop, nbottom) = newsides s newside + newsize = (nright-nleft):+(nbottom-ntop) + in BoundingBox { _centre=(nleft:+ntop)+(newsize*(0.5:+0.0)), _size=newsize } + +-- Core lenses ----------------------------------------------------------------------------------------------------------------------------- + +-- | +centre :: RealFloat f => Lens (BoundingBox f) (BoundingBox f) (Complex f) (Complex f) +centre f s = let assemble new = s { _centre=new } in assemble <$> f (_centre s) + + +-- | +size :: RealFloat f => Lens (BoundingBox f) (BoundingBox f) (Complex f) (Complex f) +size f s = let assemble new = s { _size=new } in assemble <$> f (_size s) + +-- Side lenses (absolute) ------------------------------------------------------------------------------------------------------------------ + +-- | +left :: RealFloat f => Lens (BoundingBox f) (BoundingBox f) f f +left = makeBoundingBoxSideLens + (\(BoundingBox { _centre=cx:+_, _size=dx:+_ }) -> cx - dx/2) + (\(BoundingBox { _centre=cx:+cy, _size=dx:+dy }) newside -> (newside, cx+dx/2, cy-dy/2, cy+dy/2)) + + +-- | +right :: RealFloat f => Lens (BoundingBox f) (BoundingBox f) f f +right = makeBoundingBoxSideLens + (\(BoundingBox { _centre=cx:+_, _size=dx:+_ }) -> cx + dx/2) + (\(BoundingBox { _centre=cx:+cy, _size=dx:+dy }) newside -> (cx-dx/2, newside, cy-dy/2, cy+dy/2)) + + +-- | +top :: RealFloat f => Lens (BoundingBox f) (BoundingBox f) f f +top = makeBoundingBoxSideLens + (\(BoundingBox { _centre=_:+cy, _size=_:+dy }) -> cy - dy/2) + (\(BoundingBox { _centre=cx:+cy, _size=dx:+dy }) newside -> (cx-dx/2, cx+dx/2, newside, cy+dy/2)) + + +-- | +bottom :: RealFloat f => Lens (BoundingBox f) (BoundingBox f) f f +bottom = makeBoundingBoxSideLens + (\(BoundingBox { _centre=_:+cy, _size=_:+dy }) -> cy + dy/2) + (\(BoundingBox { _centre=cx:+cy, _size=dx:+dy }) newside -> (cx-dx/2, cx+dx/2, cy-dy/2, newside)) + +-- Side lenses (relative) ------------------------------------------------------------------------------------------------------------------ + +-- | +leftpad :: RealFloat f => Lens (BoundingBox f) (BoundingBox f) f f +leftpad = makeBoundingBoxSideLens + (\(BoundingBox { _centre=cx:+_, _size=dx:+_ }) -> cx - dx/2) + (\(BoundingBox { _centre=cx:+cy, _size=dx:+dy }) newside -> (cx-dx/2+newside, cx+dx/2, cy-dy/2, cy+dy/2)) + + +-- | +rightpad :: RealFloat f => Lens (BoundingBox f) (BoundingBox f) f f +rightpad = makeBoundingBoxSideLens + (\(BoundingBox { _centre=cx:+_, _size=dx:+_ }) -> cx + dx/2) + (\(BoundingBox { _centre=cx:+cy, _size=dx:+dy }) newside -> (cx-dx/2, cx+dx/2+newside, cy-dy/2, cy+dy/2)) + + +-- | +toppad :: RealFloat f => Lens (BoundingBox f) (BoundingBox f) f f +toppad = makeBoundingBoxSideLens + (\(BoundingBox { _centre=_:+cy, _size=_:+dy }) -> cy - dy/2) + (\(BoundingBox { _centre=cx:+cy, _size=dx:+dy }) newside -> (cx-dx/2, cx+dx/2, cy-dy/2+newside, cy+dy/2)) + + +-- | +bottompad :: RealFloat f => Lens (BoundingBox f) (BoundingBox f) f f +bottompad = makeBoundingBoxSideLens + (\(BoundingBox { _centre=_:+cy, _size=_:+dy }) -> cy + dy/2) + (\(BoundingBox { _centre=cx:+cy, _size=dx:+dy }) newside -> (cx-dx/2, cx+dx/2, cy-dy/2, cy+dy/2+newside))
+ src/Cartesian/Plane/Types.hs view
@@ -0,0 +1,55 @@+-- | +-- Module : Cartesian.Plane.Types +-- Description : +-- Copyright : (c) Jonatan H Sundqvist, 2015 +-- License : MIT +-- Maintainer : Jonatan H Sundqvist +-- Stability : experimental|stable +-- Portability : POSIX (not sure) +-- + +-- Created September 6 2015 + +-- TODO | - Rename or move out function definitions +-- - Move BoundingBox functions to separate module (so that you could write BBox.makeFrom...) + +-- SPEC | - +-- - + + + +-------------------------------------------------------------------------------------------------------------------------------------------- +-- GHC Directives +-------------------------------------------------------------------------------------------------------------------------------------------- +{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE RankNTypes #-} + + + +-------------------------------------------------------------------------------------------------------------------------------------------- +-- API +-------------------------------------------------------------------------------------------------------------------------------------------- +module Cartesian.Plane.Types where + + + +-------------------------------------------------------------------------------------------------------------------------------------------- +-- We'll need these +-------------------------------------------------------------------------------------------------------------------------------------------- +import Data.Complex +import Control.Lens + + + +-------------------------------------------------------------------------------------------------------------------------------------------- +-- Types +-------------------------------------------------------------------------------------------------------------------------------------------- + +-- | +-- TODO: Anchors (eg. C, N, S, E W and combinations thereof, perhaps represented as relative Vectors) +data BoundingBox f = BoundingBox { _centre :: Complex f, _size :: Complex f } deriving (Show) + + +-- | +-- TODO: Use existing type instead (?) +-- data Side = SideLeft | SideRight | SideTop | SideBottom
+ src/Cartesian/Plane/Utilities.hs view
@@ -0,0 +1,71 @@+-- | +-- Module : Cartesian.Plane.Utilities +-- Description : +-- Copyright : (c) Jonatan H Sundqvist, 2015 +-- License : MIT +-- Maintainer : Jonatan H Sundqvist +-- Stability : experimental|stable +-- Portability : POSIX (not sure) +-- + +-- Created September 8 2015 + +-- TODO | - Uses lenses for Complex type (?) +-- - + +-- SPEC | - +-- - + + + +-------------------------------------------------------------------------------------------------------------------------------------------- +-- GHC Pragmas +-------------------------------------------------------------------------------------------------------------------------------------------- + + + +-------------------------------------------------------------------------------------------------------------------------------------------- +-- API +-------------------------------------------------------------------------------------------------------------------------------------------- +module Cartesian.Plane.Utilities where + + + +-------------------------------------------------------------------------------------------------------------------------------------------- +-- We'll need these +-------------------------------------------------------------------------------------------------------------------------------------------- +import Data.Complex + + + +-------------------------------------------------------------------------------------------------------------------------------------------- +-- Functions +-------------------------------------------------------------------------------------------------------------------------------------------- +-- | Applies a function to each component in a vector +dotmap :: (a -> b) -> Complex a -> Complex b +dotmap f (re:+im) = f re :+ f im + + +-- | +dotwise :: (a -> a -> b) -> Complex a -> Complex a -> Complex b +dotwise f (re:+im) (re':+im') = f re re':+ f im im' + + +-- | Negates the real component (X) +flipx :: Complex Double -> Complex Double +flipx (x:+y) = (-x):+y + + +-- | Negates the imaginary component (Y) +flipy :: Complex Double -> Complex Double +flipy (x:+y) = x:+(-y) + + +-- | Creates a number on the real line (where the imaginary part is 0) +real :: Double -> Complex Double +real = (:+ 0) + + +-- | Creates a number on the imaginary line (where the real part is 0) +imag :: Double -> Complex Double +imag = (0 :+)
+ src/Cartesian/Space.hs view
@@ -0,0 +1,157 @@+-- | +-- Module : Cartesian.Space +-- Copyright : (C) 2015 Jonatan H Sundqvist +-- License : MIT-style (see the file LICENSE) +-- Maintainer : Jonatan H Sundqvist <jonatanhsundqvist@gmail.com> +-- 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)