module GeomAlg.Line where
import Maybe ( isJust, fromJust )
import GeomAlg.Point ( Point ((<+>),(<*>)), xcoord, ycoord, zcoord, distance, sqrDistance )
import GeomAlg.Point2 ( Point2 (..), P2, Orientation (..), orientation, angle2 )
import GeomAlg.Point3 ( Point3 )
import qualified GeomAlg.Point2 as Point2 (translate, rotateOrg, reflect, rotate)
data (Point p, Num a) => Line p a
= Segment { point1, point2 :: p a }
| Ray { point1, point2 :: p a }
| Line { point1, point2 :: p a }
type Line2 a = Line Point2 a
type L2 a = Line Point2 a
type Line2D = Line2 Double
type Line3 a = Line Point3 a
segmentToRay, segmentToLine,
rayToLine :: (Point p, Num a) => Line p a -> Line p a
segmentToRay (Segment x y) = Ray x y
segmentToLine (Segment x y) = Line x y
rayToLine (Ray x y) = Line x y
source, target :: (Point p, Num a) => Line p a -> p a
source (Segment a _) = a
target (Segment _ b) = b
mapLine :: (Point p, Num a, Num b) => (p a -> p b) -> Line p a -> Line p b
mapLine f (Segment x y) = Segment (f x) (f y)
mapLine f (Ray x y) = Ray (f x) (f y)
mapLine f (Line x y) = Line (f x) (f y)
xcoord1, xcoord2,
ycoord1, ycoord2,
zcoord1, zcoord2 :: (Point p, Num a) => Line p a -> a
xcoord1 = xcoord . point1
ycoord1 = ycoord . point1
zcoord1 = zcoord . point1
xcoord2 = xcoord . point2
ycoord2 = ycoord . point2
zcoord2 = zcoord . point2
dx,dy :: Num a => L2 a -> a
dx s = xcoord2 s xcoord1 s
dy s = ycoord2 s ycoord1 s
isVertical, isHorizontal :: Num a => L2 a -> Bool
isVertical s = ycoord1 s == ycoord2 s
isHorizontal s = xcoord1 s == xcoord2 s
horizontal, vertical :: Num a => a -> L2 a
horizontal y = Line (Point2 (0, y)) (Point2 (1, y))
vertical x = Line (Point2 (x, 0)) (Point2 (x,1))
data Fractional a => Slope a = Vertical | Slope a
deriving (Eq, Show)
slope :: Fractional a => L2 a -> Slope a
slope s | dx' == 0 = Vertical
| otherwise = Slope (dy s / dx')
where dx' = dx s
areParallel :: Fractional a => L2 a -> L2 a -> Bool
areParallel s t = slope s == slope t
direction :: RealFloat a => L2 a -> a
direction s | a/=0 || b/=0 = atan2 b a
| otherwise = 0
where a = dx s
b = dy s
angle :: Line2D -> Line2D -> Double
angle s t = angle2 (point2 s point1 s) (point2 t point1 t)
translate :: (Floating a, Ord a) => L2 a -> a -> a -> L2 a
translate s phi d = mapLine (\ x -> Point2.translate x phi d) s
rotate :: (Floating a, Ord a) => L2 a -> P2 a -> a -> L2 a
rotate s r phi = mapLine (\ x -> Point2.rotate x r phi) s
rotateOrg :: (Floating a, Ord a) => L2 a -> a -> L2 a
rotateOrg s phi = mapLine (\ x -> Point2.rotateOrg x phi) s
reflect :: Fractional a => L2 a -> P2 a -> P2 a -> L2 a
reflect s p q = mapLine (\ x -> Point2.reflect x p q) s
fromPDL :: (Floating a, Ord a) => (P2 a -> P2 a -> b) -> P2 a -> a -> a -> b
fromPDL c p phi d = c p (p + (oriented phi d))
where oriented phi d = Point2.rotateOrg (Point2 (d, 0)) phi
orientationOfLines :: (Num a, Ord a) => L2 a -> L2 a -> Orientation
orientationOfLines s t
| p == p' = orientation p (point2 s) (point2 t)
| otherwise = error "Line2.orientationOfLines: point1 s/=point1 t"
where p = point1 s
p' = point1 t
intersect, strictIntersect :: (Ord a, Fractional a) => L2 a -> L2 a -> Maybe (Point2 a)
doIntersect,doStrictIntersect :: (Ord a, Fractional a) => L2 a -> L2 a -> Bool
intersect s1 s2
| isJust res && ok s1 r && ok s2 s = Just i
| otherwise = Nothing
where
res = intersection s1 s2
(i,r,s) = fromJust res
ok (Segment _ _) r = 0<=r && r<=1
ok (Ray _ _) r = r>=0
ok (Line _ _) r = True
doIntersect s t = isJust (intersect s t)
strictIntersect = interAux paramOk
where
paramOk (Segment _ _) r = 0<r && r<1
paramOk (Ray _ _) r = r>0
paramOk (Line _ _) r = True
doStrictIntersect s t = isJust (strictIntersect s t)
interAux :: Fractional a => (Line2 a -> a -> Bool) -> Line2 a
-> Line2 a -> Maybe (Point2 a)
interAux ok s1 s2 = if isJust res && ok s1 r && ok s2 s then Just i else Nothing
where
res = intersection s1 s2
(i,r,s) = fromJust res
intersection :: Fractional a => L2 a -> L2 a -> Maybe (Point2 a,a,a)
intersection s1 s2
| denom == 0 = Nothing
| otherwise = Just (i, r, s)
where a = point1 s1
b = point2 s1
c = point1 s2
d = point2 s2
xa = xcoord a
ya = ycoord a
xb = xcoord b
yb = ycoord b
xc = xcoord c
yc = ycoord c
xd = xcoord d
yd = ycoord d
denom = (xbxa)*(ydyc)(ybya)*(xdxc)
r = ((yayc)*(xdxc)(xaxc)*(ydyc)) / denom
s = ((yayc)*(xbxa)(xaxc)*(ybya)) / denom
i = a + (r <*> (b a))
distanceFromLine :: (Ord a, Floating a) => L2 a -> P2 a -> a
distanceFromLine l c = sqrt (sqrDistanceFromLine l c)
sqrDistanceFromLine :: (Ord a, Fractional a) => L2 a -> P2 a -> a
sqrDistanceFromLine (Line a b) c = abs (s*s*l2)
where (r,s,l2,_) = distanceAux a b c
sqrDistanceFromLine (Ray a b) c
| r >= 0 = abs (s*s*l2)
| r < 0 = sqrDistance a c
where (r,s,l2,_) = distanceAux a b c
sqrDistanceFromLine (Segment a b) c
| r < 0 = sqrDistance a c
| r > 1 = sqrDistance b c
| 0<=r && r<=1 = abs (s*s*l2)
where (r,s,l2,_) = distanceAux a b c
distanceAux :: Fractional a => P2 a -> P2 a -> P2 a -> (a,a,a,P2 a)
distanceAux a@(Point2 (xa,ya)) b@(Point2 (xb,yb)) c@(Point2 (xc,yc))
= (r, s, l2, i)
where l2 = sqrLengthOfSegment (Segment a b)
r = ((yayc)*(yayb)(xaxc)*(xbxa)) / l2
s = ((yayc)*(xbxa)(xaxc)*(ybya)) / l2
i = a + (r <*> (b a))
lengthOfSegment :: (Point p, Floating a) => Line p a -> a
lengthOfSegment (Segment p q) = distance p q
sqrLengthOfSegment :: (Point p, Num a) => Line p a -> a
sqrLengthOfSegment (Segment p q) = sqrDistance p q
centerOfSegment :: (Point p, Fractional a) => Line p a -> p a
centerOfSegment (Segment p q) = 0.5 <*> (p <+> q)
perpendicular :: Fractional a => L2 a -> L2 a
perpendicular s@(Segment p q) = Line p (p (Point2 (ycoord q ycoord p, xcoord p xcoord q)))
where c = centerOfSegment s
bisector :: Fractional a => L2 a -> L2 a
bisector s@(Segment p q) = Line c (c + (Point2 (ycoord q ycoord p, xcoord p xcoord q)))
where c = centerOfSegment s