{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE TypeFamilies #-} {-# 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 = 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 = withTrail id cutLoop . 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 = withTrail glueLine id . unLoc -- | 'Trail's are trail-like; the location is simply ignored. instance (Metric v, OrderedField n) => TrailLike (Trail v n) where trailLike = unLoc -- | Translationally invariant things are trail-like as long as the -- underlying type is. instance TrailLike t => TrailLike (TransInv t) where trailLike = view _Unwrapped' . 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 t = trailLike t `at` loc t ------------------------------------------------------------ -- Constructing TrailLike things ------------------------- ------------------------------------------------------------ -- | Construct a trail-like thing from a list of segments, with the -- origin as the location. -- -- <> -- -- > 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 = fromLocSegments . (`at` origin) -- | Construct a trail-like thing from a located list of segments. fromLocSegments :: TrailLike t => Located [Segment Closed (V t) (N t)] -> t fromLocSegments = trailLike . mapLoc trailFromSegments -- | Construct a trail-like thing of linear segments from a list -- of offsets, with the origin as the location. -- -- <> -- -- > fromOffsetsEx = fromOffsets -- > [ unitX -- > , unitX # rotateBy (1/6) -- > , unitX # rotateBy (-1/6) -- > , unitX -- > ] -- > # centerXY # pad 1.1 fromOffsets :: TrailLike t => [Vn t] -> t fromOffsets = trailLike . (`at` origin) . 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 = trailLike . mapLoc 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. -- -- <> -- -- > 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 [] = trailLike (emptyTrail `at` origin) fromVertices ps@(p:_) = trailLike (trailFromSegments (segmentsFromVertices ps) `at` p) segmentsFromVertices :: (Additive v, Num n) => [Point v n] -> [Segment Closed v n] segmentsFromVertices [] = [] segmentsFromVertices vvs@(_:vs) = map straight (zipWith (flip (.-.)) vvs vs) -- | Create a linear trail between two given points. -- -- <> -- -- > 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 p1 ~~ p2 = fromVertices [p1, 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. -- -- <> -- -- > 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, Additive v, TrailLike t) => Located (Trail v n) -> [t] explodeTrail = map (mkTrail . fromFixedSeg) . fixTrail where mkTrail = trailLike . mapLoc (trailFromSegments . (:[]))