hgeometry-0.4.0.0: src/Data/Geometry/Interval.hs
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE DeriveFunctor #-}
module Data.Geometry.Interval(
-- * 1 dimensional Intervals
Interval(..)
, Intersection(..)
-- * querying the start and end of intervals
, HasStart(..), HasEnd(..)
-- * Working with intervals
, width
, inInterval
) where
import Control.Lens
import Data.Ext
import Data.Geometry.Properties
--------------------------------------------------------------------------------
data EndPointType = Open | Closed deriving (Show, Eq, Ord)
newtype EndPoint (t :: EndPointType) a = EndPoint a deriving (Show,Read,Eq,Ord)
data Range l u a = Range { _lower :: EndPoint l a
, _upper :: EndPoint u a
} deriving (Show,Read,Eq,Ord)
-- newtype GLineSegment s t d p r = GLineSegment (GInterval s t p (Point d r))
-- newtype GInterval s t a r = GInterval (Range s t (r :+ a))
-- data CurveSegment c s t r = SubLine { _support :: c , _range :: Range s t r }
--------------------------------------------------------------------------------
data Interval a r = Interval { _start :: r :+ a
, _end :: r :+ a
}
deriving (Show,Read,Eq,Functor)
class HasStart t where
type StartCore t
type StartExtra t
start :: Lens' t (StartCore t :+ StartExtra t)
instance HasStart (Interval a r) where
type StartCore (Interval a r) = r
type StartExtra (Interval a r) = a
start = lens _start (\(Interval _ e) s -> Interval s e)
class HasEnd t where
type EndCore t
type EndExtra t
end :: Lens' t (EndCore t :+ EndExtra t)
instance HasEnd (Interval a r) where
type EndCore (Interval a r) = r
type EndExtra (Interval a r) = a
end = lens _end (\(Interval s _) e -> Interval s e)
-- | When comparing intervals, compare them lexicographically on
-- (start^.core,end^.core,start^.extra,end^.extra)
instance (Ord a, Ord r) => Ord (Interval a r) where
(Interval (s :+ a) (t :+ b)) `compare` (Interval (p :+ c) (q :+ d)) =
(s,t,a,b) `compare` (p,q,c,d)
type instance Dimension (Interval a r) = 1
type instance NumType (Interval a r) = r
instance Ord r => IsIntersectableWith (Interval a r) (Interval a r) where
data Intersection (Interval a r) (Interval a r) = IntervalIntersection (Interval a r)
| NoOverlap
deriving (Show,Read,Eq,Ord)
nonEmptyIntersection NoOverlap = False
nonEmptyIntersection _ = True
(Interval a b) `intersect` (Interval c d)
| s^.core <= t^.core = IntervalIntersection $ Interval s t
| otherwise = NoOverlap
where
s = a `maxOnCore` c
t = b `minOnCore` d
instance Ord r => IsUnionableWith (Interval a r) (Interval a r) where
data Union (Interval a r) (Interval a r) = DisjointIntervals (Interval a r) (Interval a r)
| OneInterval (Interval a r)
deriving (Show,Read,Eq,Ord)
i@(Interval a b) `union` j@(Interval c d)
| i `intersects` j = OneInterval $ Interval s t
| otherwise = DisjointIntervals i j
where
s = a `minOnCore` c
t = b `maxOnCore` d
maxOnCore :: Ord c => c :+ e -> c :+ e -> c :+ e
l@(lc :+ _) `maxOnCore` r@(rc :+ _) = if lc >= rc then l else r
minOnCore :: Ord c => c :+ e -> c :+ e -> c :+ e
l@(lc :+ _) `minOnCore` r@(rc :+ _) = if lc <= rc then l else r
-- | Get the width of the interval
width :: Num r => Interval a r -> r
width i = i^.end.core - i^.start.core
inInterval :: Ord r => r -> Interval a r -> Bool
x `inInterval` i = i^.start.core <= x && x <= i^.end.core