geom2d-0.1.0.1: Geom2d/Line.hs
-- | This module describes what lines are defines functions to work
-- with lines.
module Geom2d.Line
( -- * Infinite lines
InfLine
, mkInfLine
, lineF
, parallel
, slope
, root
, intersection
-- * Finite lines
, FinLine
, mkFinLine
, lineLength
)
where
import Geom2d.Point
import Geom2d.Line.Internal
-- | Construct an infinit line by specifiying two points. We won't
-- get a line when the given points are equal.
mkInfLine :: (Eq (p a)) => p a -> p a -> Maybe (InfLine p a)
mkInfLine a b
| a == b = Nothing
| otherwise = Just $ InfLine a b
-- | Get a function describing the line. We won't get a function if
-- the line is vertical.
lineF :: (Eq a, Fractional a, Point p) =>
InfLine p a -> Maybe (a -> a)
lineF l@(InfLine p _) =
( \m -> case root l of
Just x0 -> \arg -> m * (arg - x0)
Nothing -> const (y p) ) <$>
slope l
-- | Check if two lines are paralllel to each other. This function
-- assumes lines parallel to themselves.
parallel :: (Num a, Num (p a), Point p, Eq a) =>
InfLine p a -> InfLine p a -> Bool
parallel (InfLine a b) (InfLine p q) = (b - a) `cross` (q - p) == 0
-- | Calculate the slope of a line. We won't get a value for the
-- slope if, and only if, the line is vertical.
slope :: (Fractional a, Point p, Eq a) =>
InfLine p a -> Maybe a
slope (InfLine p q)
| x p == x q = Nothing
| otherwise = Just $ (y q - y p) / (x q - x p)
-- | Calculate the point where a line meets the x-axis. We won't get
-- a value if, and only if the line is parallel to the x-axis.
root :: (Eq a, Fractional a, Point p) =>
InfLine p a -> Maybe a
root l@(InfLine p _) =
case slope l of
Nothing -> Just $ x p
Just m ->
if m == 0
then Nothing
else Just $ x p - (y p * m)
-- | Calculate the point where two lines intersect.
intersection :: (Eq (p a), Num (p a), RealFloat a, Point p) =>
InfLine p a -> InfLine p a -> Maybe (p a)
intersection l1@(InfLine a1 _) l2@(InfLine b1 _)
| l1 == l2 = Nothing
| l1 `parallel` l2 = Nothing
| otherwise =
case slope l1 of
Nothing -> do
x0 <- root l1
f <- lineF l2
return (fromCoords x0 (f x0))
Just ma ->
case slope l2 of
Nothing -> do
x0 <- root l2
f <- lineF l1
return (fromCoords x0 (f x0))
Just mb -> do
let na = y a1 - ma * x a1
nb = y b1 - mb * x b1
safeDiv num denom | denom == 0 = Nothing
| otherwise = Just (num/denom)
x' <- (nb - na) `safeDiv` (ma - mb)
f <- lineF l1
return (fromCoords x' (f x'))
-- | mkFinLine returns a valid finite line, if any.
mkFinLine :: (Eq (p a)) => p a -> p a -> Maybe (FinLine p a)
mkFinLine a b
| a == b = Nothing
| otherwise = Just (FinLine a b)
-- | Get the length of a finite line.
lineLength :: (Point p, Num (p a), Floating a) => FinLine p a -> a
lineLength (FinLine a b) = magnitude (b - a)