{-# OPTIONS_GHC -Wno-orphans #-}
--------------------------------------------------------------------------------
-- |
-- Module      :  HGeometry.Line
-- Copyright   :  (C) Frank Staals
-- License     :  see the LICENSE file
-- Maintainer  :  Frank Staals
--
-- Lines in d-dimensional space.
--
--------------------------------------------------------------------------------
module HGeometry.Line
  ( module HGeometry.Line.Class
  , module HGeometry.Line.LineEQ
  , module HGeometry.Line.PointAndVector
  , module HGeometry.Line.General
  , LineLineIntersection, LineLineIntersectionG(..)
  , fromLineEQ
  ) where

import Control.Lens
import HGeometry.Intersection
import HGeometry.Line.Class
import HGeometry.Line.Intersection
import HGeometry.Line.LineEQ
import HGeometry.Line.PointAndVector hiding (liesAbove, liesBelow)
import HGeometry.Point
import HGeometry.Vector
import HGeometry.Line.General

--------------------------------------------------------------------------------


type instance Intersection (LineEQ r) (LinePV 2 r) =
  Maybe (LineLineIntersection (LineEQ r))

instance (Eq r, Num r) => HasIntersectionWith (LineEQ r) (LinePV 2 r) where
  (LineEQ r
a r
b) intersects :: LineEQ r -> LinePV 2 r -> Bool
`intersects` (LinePV Point 2 r
p Vector 2 r
v) = Bool
differentSlopes Bool -> Bool -> Bool
|| Bool
sameLine
    where
      differentSlopes :: Bool
differentSlopes = (r
ar -> r -> r
forall a. Num a => a -> a -> a
* Vector 2 r
vVector 2 r -> Getting r (Vector 2 r) r -> r
forall s a. s -> Getting a s a -> a
^.Getting r (Vector 2 r) r
forall vector (d :: Natural) r.
(Vector_ vector d r, 0 <= (d - 1)) =>
IndexedLens' Int vector r
IndexedLens' Int (Vector 2 r) r
xComponent) r -> r -> Bool
forall a. Eq a => a -> a -> Bool
/= Vector 2 r
vVector 2 r -> Getting r (Vector 2 r) r -> r
forall s a. s -> Getting a s a -> a
^.Getting r (Vector 2 r) r
forall vector (d :: Natural) r.
(Vector_ vector d r, 1 <= (d - 1)) =>
IndexedLens' Int vector r
IndexedLens' Int (Vector 2 r) r
yComponent
      sameLine :: Bool
sameLine        = r
ar -> r -> r
forall a. Num a => a -> a -> a
*(Point 2 r
pPoint 2 r -> Getting r (Point 2 r) r -> r
forall s a. s -> Getting a s a -> a
^.Getting r (Point 2 r) r
forall (d :: Natural) point r.
(1 <= d, Point_ point d r) =>
IndexedLens' Int point r
IndexedLens' Int (Point 2 r) r
xCoord) r -> r -> r
forall a. Num a => a -> a -> a
+ r
b r -> r -> Bool
forall a. Eq a => a -> a -> Bool
== Point 2 r
pPoint 2 r -> Getting r (Point 2 r) r -> r
forall s a. s -> Getting a s a -> a
^.Getting r (Point 2 r) r
forall (d :: Natural) point r.
(2 <= d, Point_ point d r) =>
IndexedLens' Int point r
IndexedLens' Int (Point 2 r) r
yCoord
      -- if the slopes are equal, but lines go through the same point so they are the same
      -- line, and thus they intersect
  {-# INLINE intersects #-}

instance (Ord r, Fractional r)
         => IsIntersectableWith (LineEQ r) (LinePV 2 r) where
  LineEQ r
l intersect :: LineEQ r -> LinePV 2 r -> Intersection (LineEQ r) (LinePV 2 r)
`intersect` LinePV 2 r
m = case LinePV 2 r -> Maybe (LineEQ r)
forall r. (Fractional r, Ord r) => LinePV 2 r -> Maybe (LineEQ r)
toLinearFunction LinePV 2 r
m of
    Maybe (LineEQ r)
Nothing    -> let x :: r
x = LinePV 2 r
mLinePV 2 r -> Getting r (LinePV 2 r) r -> r
forall s a. s -> Getting a s a -> a
^.(Point 2 r -> Const r (Point 2 r))
-> LinePV 2 r -> Const r (LinePV 2 r)
forall (d :: Natural) r (f :: * -> *).
Functor f =>
(Point d r -> f (Point d r)) -> LinePV d r -> f (LinePV d r)
anchorPoint((Point 2 r -> Const r (Point 2 r))
 -> LinePV 2 r -> Const r (LinePV 2 r))
-> ((r -> Const r r) -> Point 2 r -> Const r (Point 2 r))
-> Getting r (LinePV 2 r) r
forall b c a. (b -> c) -> (a -> b) -> a -> c
.(r -> Const r r) -> Point 2 r -> Const r (Point 2 r)
forall (d :: Natural) point r.
(1 <= d, Point_ point d r) =>
IndexedLens' Int point r
IndexedLens' Int (Point 2 r) r
xCoord
                  in LineLineIntersectionG r (LineEQ r)
-> Maybe (LineLineIntersectionG r (LineEQ r))
LineLineIntersectionG r (LineEQ r)
-> Intersection (LineEQ r) (LinePV 2 r)
forall a. a -> Maybe a
Just (LineLineIntersectionG r (LineEQ r)
 -> Intersection (LineEQ r) (LinePV 2 r))
-> (Point 2 r -> LineLineIntersectionG r (LineEQ r))
-> Point 2 r
-> Intersection (LineEQ r) (LinePV 2 r)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Point 2 r -> LineLineIntersectionG r (LineEQ r)
forall r line. Point 2 r -> LineLineIntersectionG r line
Line_x_Line_Point (Point 2 r -> Intersection (LineEQ r) (LinePV 2 r))
-> Point 2 r -> Intersection (LineEQ r) (LinePV 2 r)
forall a b. (a -> b) -> a -> b
$ r -> r -> Point 2 r
forall r. r -> r -> Point 2 r
Point2 r
x (r -> LineEQ r -> r
forall r. Num r => r -> LineEQ r -> r
evalAt' r
x LineEQ r
l)
                  -- m is vertical, l is not, so they intersect in a point
    Just LineEQ r
m'    -> LineEQ r
l LineEQ r -> LineEQ r -> Intersection (LineEQ r) (LineEQ r)
forall g h. IsIntersectableWith g h => g -> h -> Intersection g h
`intersect` LineEQ r
m'

-- | Convert from a LineEQ to a Point and Line
fromLineEQ              :: Num r => LineEQ r -> LinePV 2 r
fromLineEQ :: forall r. Num r => LineEQ r -> LinePV 2 r
fromLineEQ (LineEQ r
a r
b) = r -> r -> LinePV 2 r
forall r. Num r => r -> r -> LinePV 2 r
fromLinearFunction r
a r
b

instance Fractional r => HasSquaredEuclideanDistance (LineEQ r) where
  pointClosestTo :: forall r (d :: Natural) point.
(r ~ NumType (LineEQ r), d ~ Dimension (LineEQ r), Num r,
 Point_ point d r) =>
point -> LineEQ r -> Point d r
pointClosestTo point
q LineEQ r
l = point -> LinePV 2 r -> Point d r
forall g r (d :: Natural) point.
(HasSquaredEuclideanDistance g, r ~ NumType g, d ~ Dimension g,
 Num r, Point_ point d r) =>
point -> g -> Point d r
forall r (d :: Natural) point.
(r ~ NumType (LinePV 2 r), d ~ Dimension (LinePV 2 r), Num r,
 Point_ point d r) =>
point -> LinePV 2 r -> Point d r
pointClosestTo point
q (LineEQ r -> LinePV 2 r
forall r. Num r => LineEQ r -> LinePV 2 r
fromLineEQ LineEQ r
l)