{-# LANGUAGE ConstraintKinds      #-}
{-# LANGUAGE FlexibleContexts     #-}
{-# LANGUAGE FlexibleInstances    #-}
{-# LANGUAGE TypeFamilies         #-}
{-# LANGUAGE TypeOperators        #-}
{-# LANGUAGE UndecidableInstances #-}
-----------------------------------------------------------------------------
-- |
-- Module      :  Diagrams.TrailLike
-- Copyright   :  (c) 2013 diagrams-lib team (see LICENSE)
-- License     :  BSD-style (see LICENSE)
-- Maintainer  :  diagrams-discuss@googlegroups.com
--
-- The 'TrailLike' class abstracts over anything which can be
-- constructed from a concretely located 'Trail', including
-- lines, loops, trails, paths, vertex lists, and diagrams.
--
-----------------------------------------------------------------------------

module Diagrams.TrailLike
       (
         -- * The TrailLike class

         TrailLike(..)

         -- * Constructing TrailLikes

       , fromSegments, fromLocSegments, fromOffsets, fromLocOffsets, fromVertices
       , (~~), explodeTrail

       ) where

import           Control.Lens     (view, _Unwrapped')

import           Diagrams.Core
import           Diagrams.Located
import           Diagrams.Segment
import           Diagrams.Trail

import           Linear.Affine
import           Linear.Metric
import           Linear.Vector

------------------------------------------------------------
--  TrailLike class
------------------------------------------------------------

-- | A type class for trail-like things, /i.e./ things which can be
--   constructed from a concretely located 'Trail'.  Instances include
--   lines, loops, trails, paths, lists of vertices, two-dimensional
--   'Diagram's, and 'Located' variants of all the above.
--
--   Usually, type variables with 'TrailLike' constraints are used as
--   the /output/ types of functions, like
--
--   @
--   foo :: (TrailLike t) => ... -> t
--   @
--
--   Functions with such a type can be used to construct trails,
--   paths, diagrams, lists of points, and so on, depending on the
--   context.
--
--   To write a function with a signature like the above, you can of
--   course call 'trailLike' directly; more typically, one would use
--   one of the provided functions like 'fromOffsets', 'fromVertices',
--   'fromSegments', or '~~'.
class (Metric (V t), OrderedField (N t)) => TrailLike t where

  trailLike
    :: Located (Trail (V t) (N t)) -- ^ The concretely located trail.  Note
                                   --   that some trail-like things
                                   --   (e.g. 'Trail's) may ignore the
                                   --   location.
    -> t

------------------------------------------------------------
--  Instances  ---------------------------------------------

-- | A list of points is trail-like; this instance simply
--   computes the vertices of the trail, using 'trailPoints'.
instance (Metric v, OrderedField n) => TrailLike [Point v n] where
  trailLike :: Located (Trail (V [Point v n]) (N [Point v n])) -> [Point v n]
trailLike = forall (v :: * -> *) n.
(Metric v, OrderedField n) =>
Located (Trail v n) -> [Point v n]
trailPoints

-- | Lines are trail-like.  If given a 'Trail' which contains a loop,
--   the loop will be cut with 'cutLoop'.  The location is ignored.
instance (Metric v, OrderedField n) => TrailLike (Trail' Line v n) where
  trailLike :: Located (Trail (V (Trail' Line v n)) (N (Trail' Line v n)))
-> Trail' Line v n
trailLike = forall (v :: * -> *) n r.
(Trail' Line v n -> r) -> (Trail' Loop v n -> r) -> Trail v n -> r
withTrail forall a. a -> a
id forall (v :: * -> *) n.
(Metric v, OrderedField n) =>
Trail' Loop v n -> Trail' Line v n
cutLoop forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Located a -> a
unLoc

-- | Loops are trail-like.  If given a 'Trail' containing a line, the
--   line will be turned into a loop using 'glueLine'.  The location
--   is ignored.
instance (Metric v, OrderedField n) => TrailLike (Trail' Loop v n) where
  trailLike :: Located (Trail (V (Trail' Loop v n)) (N (Trail' Loop v n)))
-> Trail' Loop v n
trailLike = forall (v :: * -> *) n r.
(Trail' Line v n -> r) -> (Trail' Loop v n -> r) -> Trail v n -> r
withTrail forall (v :: * -> *) n.
(Metric v, OrderedField n) =>
Trail' Line v n -> Trail' Loop v n
glueLine forall a. a -> a
id forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Located a -> a
unLoc

-- | 'Trail's are trail-like; the location is simply ignored.
instance (Metric v, OrderedField n) => TrailLike (Trail v n) where
  trailLike :: Located (Trail (V (Trail v n)) (N (Trail v n))) -> Trail v n
trailLike = forall a. Located a -> a
unLoc

-- | Translationally invariant things are trail-like as long as the
--   underlying type is.
instance TrailLike t => TrailLike (TransInv t) where
  trailLike :: Located (Trail (V (TransInv t)) (N (TransInv t))) -> TransInv t
trailLike = forall s (m :: * -> *) a. MonadReader s m => Getting a s a -> m a
view forall s. Wrapped s => Iso' (Unwrapped s) s
_Unwrapped' forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall t. TrailLike t => Located (Trail (V t) (N t)) -> t
trailLike

-- | 'Located' things are trail-like as long as the underlying type
--   is.  The location is taken to be the location of the input
--   located trail.
instance TrailLike t => TrailLike (Located t) where
  trailLike :: Located (Trail (V (Located t)) (N (Located t))) -> Located t
trailLike Located (Trail (V (Located t)) (N (Located t)))
t = forall t. TrailLike t => Located (Trail (V t) (N t)) -> t
trailLike Located (Trail (V (Located t)) (N (Located t)))
t forall a. a -> Point (V a) (N a) -> Located a
`at` forall a. Located a -> Point (V a) (N a)
loc Located (Trail (V (Located t)) (N (Located t)))
t

------------------------------------------------------------
--  Constructing TrailLike things  -------------------------
------------------------------------------------------------

-- | Construct a trail-like thing from a list of segments, with the
--   origin as the location.
--
--   <<diagrams/src_Diagrams_TrailLike_fromSegmentsEx.svg#diagram=fromSegmentsEx&height=200>>
--
--   > fromSegmentsEx = fromSegments
--   >   [ straight (r2 (1,1))
--   >   , bézier3  (r2 (1,1)) unitX unit_Y
--   >   , straight unit_X
--   >   ]
--   >   # centerXY # pad 1.1
fromSegments :: TrailLike t => [Segment Closed (V t) (N t)] -> t
fromSegments :: forall t. TrailLike t => [Segment Closed (V t) (N t)] -> t
fromSegments = forall t. TrailLike t => Located [Segment Closed (V t) (N t)] -> t
fromLocSegments forall b c a. (b -> c) -> (a -> b) -> a -> c
. (forall a. a -> Point (V a) (N a) -> Located a
`at` forall (f :: * -> *) a. (Additive f, Num a) => Point f a
origin)

-- | Construct a trail-like thing from a located list of segments.
fromLocSegments :: TrailLike t => Located [Segment Closed (V t) (N t)] -> t
fromLocSegments :: forall t. TrailLike t => Located [Segment Closed (V t) (N t)] -> t
fromLocSegments = forall t. TrailLike t => Located (Trail (V t) (N t)) -> t
trailLike forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. SameSpace a b => (a -> b) -> Located a -> Located b
mapLoc forall (v :: * -> *) n.
(Metric v, OrderedField n) =>
[Segment Closed v n] -> Trail v n
trailFromSegments

-- | Construct a trail-like thing of linear segments from a list
--   of offsets, with the origin as the location.
--
--   <<diagrams/src_Diagrams_TrailLike_fromOffsetsEx.svg#diagram=fromOffsetsEx&width=300>>
--
--   > fromOffsetsEx = fromOffsets
--   >   [ unitX
--   >   , unitX # rotateBy (1/6)
--   >   , unitX # rotateBy (-1/6)
--   >   , unitX
--   >   ]
--   >   # centerXY # pad 1.1
fromOffsets :: TrailLike t => [Vn t] -> t
fromOffsets :: forall t. TrailLike t => [Vn t] -> t
fromOffsets = forall t. TrailLike t => Located (Trail (V t) (N t)) -> t
trailLike forall b c a. (b -> c) -> (a -> b) -> a -> c
. (forall a. a -> Point (V a) (N a) -> Located a
`at` forall (f :: * -> *) a. (Additive f, Num a) => Point f a
origin) forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (v :: * -> *) n.
(Metric v, OrderedField n) =>
[v n] -> Trail v n
trailFromOffsets

-- | Construct a trail-like thing of linear segments from a located
--   list of offsets.
fromLocOffsets :: (V t ~ v, N t ~ n, V (v n) ~ v, N (v n) ~ n, TrailLike t) => Located [v n] -> t
fromLocOffsets :: forall t (v :: * -> *) n.
(V t ~ v, N t ~ n, V (v n) ~ v, N (v n) ~ n, TrailLike t) =>
Located [v n] -> t
fromLocOffsets = forall t. TrailLike t => Located (Trail (V t) (N t)) -> t
trailLike forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. SameSpace a b => (a -> b) -> Located a -> Located b
mapLoc forall (v :: * -> *) n.
(Metric v, OrderedField n) =>
[v n] -> Trail v n
trailFromOffsets

-- | Construct a trail-like thing connecting the given vertices with
--   linear segments, with the first vertex as the location.  If no
--   vertices are given, the empty trail is used with the origin as
--   the location.
--
--   <<diagrams/src_Diagrams_TrailLike_fromVerticesEx.svg#diagram=fromVerticesEx&width=300>>
--
--   > import Data.List (transpose)
--   >
--   > fromVerticesEx =
--   >   ( [ pentagon 1
--   >     , pentagon 1.3 # rotateBy (1/15)
--   >     , pentagon 1.5 # rotateBy (2/15)
--   >     ]
--   >     # transpose
--   >     # concat
--   >   )
--   >   # fromVertices
--   >   # closeTrail # strokeTrail
--   >   # centerXY # pad 1.1
fromVertices :: TrailLike t => [Point (V t) (N t)] -> t
fromVertices :: forall t. TrailLike t => [Point (V t) (N t)] -> t
fromVertices []       = forall t. TrailLike t => Located (Trail (V t) (N t)) -> t
trailLike (forall (v :: * -> *) n. (Metric v, OrderedField n) => Trail v n
emptyTrail forall a. a -> Point (V a) (N a) -> Located a
`at` forall (f :: * -> *) a. (Additive f, Num a) => Point f a
origin)
fromVertices ps :: [Point (V t) (N t)]
ps@(Point (V t) (N t)
p:[Point (V t) (N t)]
_) = forall t. TrailLike t => Located (Trail (V t) (N t)) -> t
trailLike (forall (v :: * -> *) n.
(Metric v, OrderedField n) =>
[Segment Closed v n] -> Trail v n
trailFromSegments (forall (v :: * -> *) n.
(Additive v, Num n) =>
[Point v n] -> [Segment Closed v n]
segmentsFromVertices [Point (V t) (N t)]
ps) forall a. a -> Point (V a) (N a) -> Located a
`at` Point (V t) (N t)
p)

segmentsFromVertices :: (Additive v, Num n) => [Point v n] -> [Segment Closed v n]
segmentsFromVertices :: forall (v :: * -> *) n.
(Additive v, Num n) =>
[Point v n] -> [Segment Closed v n]
segmentsFromVertices []         = []
segmentsFromVertices vvs :: [Point v n]
vvs@(Point v n
_:[Point v n]
vs) = forall a b. (a -> b) -> [a] -> [b]
map forall (v :: * -> *) n. v n -> Segment Closed v n
straight (forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
zipWith (forall a b c. (a -> b -> c) -> b -> a -> c
flip forall (p :: * -> *) a. (Affine p, Num a) => p a -> p a -> Diff p a
(.-.)) [Point v n]
vvs [Point v n]
vs)

-- | Create a linear trail between two given points.
--
--   <<diagrams/src_Diagrams_TrailLike_twiddleEx.svg#diagram=twiddleEx&width=300>>
--
--   > twiddleEx
--   >   = mconcat ((~~) <$> hexagon 1 <*> hexagon 1)
--   >   # centerXY # pad 1.1
(~~) :: (V t ~ v, N t ~ n, TrailLike t) => Point v n -> Point v n -> t
Point v n
p1 ~~ :: forall t (v :: * -> *) n.
(V t ~ v, N t ~ n, TrailLike t) =>
Point v n -> Point v n -> t
~~ Point v n
p2 = forall t. TrailLike t => [Point (V t) (N t)] -> t
fromVertices [Point v n
p1, Point v n
p2]

-- | Given a concretely located trail, \"explode\" it by turning each
--   segment into its own separate trail.  Useful for (say) applying a
--   different style to each segment.
--
--   <<diagrams/src_Diagrams_TrailLike_explodeTrailEx.svg#diagram=explodeTrailEx&width=300>>
--
--   > explodeTrailEx
--   >   = pentagon 1
--   >   # explodeTrail  -- generate a list of diagrams
--   >   # zipWith lc [orange, green, yellow, red, blue]
--   >   # mconcat # centerXY # pad 1.1
explodeTrail :: (V t ~ v, N t ~ n, TrailLike t) => Located (Trail v n) -> [t]
explodeTrail :: forall t (v :: * -> *) n.
(V t ~ v, N t ~ n, TrailLike t) =>
Located (Trail v n) -> [t]
explodeTrail = forall a b. (a -> b) -> [a] -> [b]
map (Located (Segment Closed (V t) (N t)) -> t
mkTrail forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall n (v :: * -> *).
(Num n, Additive v) =>
FixedSegment v n -> Located (Segment Closed v n)
fromFixedSeg) forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (v :: * -> *) n.
(Metric v, OrderedField n) =>
Located (Trail v n) -> [FixedSegment v n]
fixTrail
  where
    mkTrail :: Located (Segment Closed (V t) (N t)) -> t
mkTrail = forall t. TrailLike t => Located (Trail (V t) (N t)) -> t
trailLike forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. SameSpace a b => (a -> b) -> Located a -> Located b
mapLoc (forall (v :: * -> *) n.
(Metric v, OrderedField n) =>
[Segment Closed v n] -> Trail v n
trailFromSegments forall b c a. (b -> c) -> (a -> b) -> a -> c
. (forall a. a -> [a] -> [a]
:[]))