module Point ( Point , pattern Point , xVal , yVal , fromFloat , pointMap , pointAsTuple -- * Operators for doing arithmetic on a point with a scalar float value , (|+|) , (|*|) , (|/|) -- * Geometric point manipulation functions , cross , dot , angleBetween , mag , magSquared , mirrorP , rotateP , cartesianProduct ) where import ApproxEq -- | A point in 2-d space: -- -- @Point 3 4@ data Point = Point !Float !Float deriving (Eq, Show) instance Num Point where Point x1 y1 + Point x2 y2 = Point (x1 + x2) (y1 + y2) Point x1 y1 * Point x2 y2 = Point (x1 * x2) (y1 * y2) Point x1 y1 - Point x2 y2 = Point (x1 - x2) (y1 - y2) abs (Point x y) = Point (abs x) (abs y) signum (Point x y) = Point (signum x) (signum y) negate (Point x y) = Point (x * (-1)) (y * (-1)) fromInteger i = Point (fromInteger i) (fromInteger i) instance Fractional Point where Point x1 y1 / Point x2 y2 = Point (x1 / x2) (y1 / y2) recip (Point x y) = Point y x fromRational r = Point (fromRational r) (fromRational r) instance Floating Point where pi = Point pi pi exp (Point x y) = Point (exp x) (exp y) log (Point x y) = Point (log x) (log y) sin (Point x y) = Point (sin x) (sin y) cos (Point x y) = Point (cos x) (cos y) asin (Point x y) = Point (asin x) (asin y) acos (Point x y) = Point (acos x) (acos y) atan (Point x y) = Point (atan x) (atan y) sinh (Point x y) = Point (sinh x) (sinh y) cosh (Point x y) = Point (cosh x) (cosh y) asinh (Point x y) = Point (asinh x) (asinh y) acosh (Point x y) = Point (acosh x) (acosh y) atanh (Point x y) = Point (atanh x) (atanh y) instance ApproxEq Point where approxEqual a b epsilon = let Point dx dy = abs (a - b) in dx < epsilon && dy < epsilon instance Ord Point where compare a b = pointAsTuple a `compare` pointAsTuple b (<=) a b = pointAsTuple a <= pointAsTuple b -- | Extract the x axis value from a point xVal :: Point -> Float xVal (Point coord _) = coord -- | Extract the y axis value from a point yVal :: Point -> Float yVal (Point _ coord) = coord -- | Construct a point from a scalar value. fromFloat :: Float -> Point fromFloat f = Point f f -- | Map a float -> float function over a point. pointMap :: (Float -> Float) -> Point -> Point pointMap f (Point x y) = Point (f x) (f y) -- | Convert a point to a 2-tuple representation. pointAsTuple :: Point -> (Float, Float) pointAsTuple (Point x y) = (x, y) -- | Add a scalar value to a point: -- -- >>> Point 1 2 |+| 5 -- Point 6 7 (|+|) :: Point -> Float -> Point (|+|) p v = p + fromFloat v -- | Multiply a point by a scalar value: -- -- >>> Point 1 2 |*| 5 -- Point 5 10 (|*|) :: Point -> Float -> Point (|*|) p v = p * fromFloat v -- | Divide a point by a scalar value: -- -- >>> Point 10 10 |/| 5 -- Point 2 2 (|/|) :: Point -> Float -> Point (|/|) p v = p / fromFloat v cross :: Point -> Point -> Float cross (Point x1 y1) (Point x2 y2) = x1 * y2 - y1 * x2 dot :: Point -> Point -> Float dot (Point x1 y1) (Point x2 y2) = x1 * x2 + y1 * y2 angleBetween :: Point -> Point -> Float angleBetween p1 p2 = atan2 (cross p1 p2) (dot p1 p2) mag :: Point -> Float mag p = sqrt (dot p p) magSquared :: Point -> Float magSquared p = dot p p -- | Mirror point a about a line through point p along vector v mirrorP :: Point -> Point -> Point -> Point mirrorP a p v = (-a) + 2 * p + 2 * w |*| dot (a - p) w where w = v |/| mag v -- | Rotate point a about a line through point p along vector t rotateP :: Point -> Point -> Float -> Point rotateP a p t = p + Point (ax * cos t - ay * sin t) (ax * sin t + ay * cos t) where Point ax ay = a - p -- | Return the cartesian product of a set of coordinates on the X axis -- and a set of coordinates on the Y axis as a series of points. cartesianProduct :: [Float] -> [Float] -> [Point] cartesianProduct xs ys = Point <$> xs <*> ys