diff --git a/diagrams-core.cabal b/diagrams-core.cabal index 9dadf32..15163a0 100644 --- a/diagrams-core.cabal +++ b/diagrams-core.cabal @@ -41,11 +41,12 @@ Library unordered-containers >= 0.2 && < 0.2.6, semigroups >= 0.8.4 && < 0.17, monoid-extras >= 0.3 && < 0.5, - dual-tree >= 0.2 && < 0.3, - lens >= 4.0 && < 4.12, + dual-tree >= 0.3 && < 0.4, + lens >= 4.0 && < 4.13, linear >= 1.11.3 && < 1.19, adjunctions >= 4.0 && < 5.0, distributive >=0.2.2 && < 1.0, + hashable, mtl hs-source-dirs: src diff --git a/src/Diagrams/Core/Compile.hs b/src/Diagrams/Core/Compile.hs index ee4c203..44dc7dc 100644 --- a/src/Diagrams/Core/Compile.hs +++ b/src/Diagrams/Core/Compile.hs @@ -1,5 +1,6 @@ {-# LANGUAGE CPP #-} {-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE LambdaCase #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeOperators #-} @@ -18,164 +19,119 @@ module Diagrams.Core.Compile ( -- * Tools for backends - RNode(..) - , RTree - , toRTree + foldDia + , foldDia' -- * Backend API , renderDia , renderDiaT - - -- * Internals - - , toDTree - , fromDTree ) where -import Data.Typeable -import qualified Data.List.NonEmpty as NEL -import Data.Maybe (fromMaybe) +import Control.Lens hiding (transform) +import qualified Data.Foldable as F import Data.Monoid.Coproduct -import Data.Monoid.MList +import qualified Data.Monoid as M import Data.Monoid.WithSemigroup (Monoid') -import Data.Semigroup -import Data.Tree -import Data.Tree.DUAL +import Data.Tree.DUAL (foldDUAL, foldDUAL') +import Data.Typeable -import Diagrams.Core.Envelope (OrderedField, diameter) +import Diagrams.Core.Envelope (OrderedField, size) +import Diagrams.Core.Style import Diagrams.Core.Transform import Diagrams.Core.Types -import Diagrams.Core.Style -import Linear.Metric hiding (qd) +import Linear.Metric hiding (qd) -- Typeable1 is a depreciated synonym in ghc > 707 #if __GLASGOW_HASKELL__ >= 707 #define Typeable1 Typeable #endif -emptyDTree :: Tree (DNode b v n a) -emptyDTree = Node DEmpty [] - -uncurry3 :: (a -> b -> c -> r) -> (a, b, c) -> r -uncurry3 f (x, y, z) = f x y z - --- | Convert a @QDiagram@ into a raw tree. -toDTree :: (HasLinearMap v, Floating n, Typeable n) - => n -> n -> QDiagram b v n m -> Maybe (DTree b v n Annotation) -toDTree g n (QD qd) - = foldDUAL - - -- Prims at the leaves. We ignore the accumulated d-annotations - -- for prims (since we instead distribute them incrementally - -- throughout the tree as they occur), or pass them to the - -- continuation in the case of a delayed node. - (\d -> withQDiaLeaf - - -- Prim: make a leaf node - (\p -> Node (DPrim p) []) - - -- Delayed tree: pass the accumulated d-annotations to - -- the continuation, convert the result to a DTree, and - -- splice it in, adding a DDelay node to mark the point - -- of the splice. - (Node DDelay . (:[]) . fromMaybe emptyDTree . toDTree g n . ($ (d, g, n)) . uncurry3) - ) - - -- u-only leaves --> empty DTree. We don't care about the - -- u-annotations. - emptyDTree - - -- a non-empty list of child trees. - (\ts -> case NEL.toList ts of - [t] -> t - ts' -> Node DEmpty ts' - ) - - -- Internal d-annotations. We untangle the interleaved - -- transformations and style, and carefully place the style - -- /above/ the transform in the tree (since by calling - -- 'untangle' we have already performed the action of the - -- transform on the style). - (\d t -> case get d of - Option Nothing -> t - Option (Just d') -> - let (tr,sty) = untangle d' - in Node (DStyle sty) [Node (DTransform tr) [t]] - ) - - -- Internal a-annotations. - (\a t -> Node (DAnnot a) [t]) - qd - --- | Convert a @DTree@ to an @RTree@ which can be used dirctly by backends. --- A @DTree@ includes nodes of type @DTransform (Transformation v)@; --- in the @RTree@ transform is pushed down until it reaches a primitive node. -fromDTree :: forall b v n. (Floating n, HasLinearMap v) - => DTree b v n Annotation -> RTree b v n Annotation -fromDTree = fromDTree' mempty +foldDiaWithScales + :: (HasLinearMap v, Floating n, Typeable n, M.Monoid r) + => (Style v n -> Prim b v n -> r) + -> (Annotation b v n -> r -> r) + -> n -- 'global' to 'output' scale factor + -> n -- 'normalised' to 'output' scale factor + -> QDiagram b v n m -- ^ diagram to fold + -> r +foldDiaWithScales primF aF g n (QD dual) = foldDUAL lF aF dual where - fromDTree' :: HasLinearMap v => Transformation v n -> DTree b v n Annotation -> RTree b v n Annotation - -- We put the accumulated transformation (accTr) and the prim - -- into an RPrim node. - fromDTree' accTr (Node (DPrim p) _) - = Node (RPrim (transform accTr p)) [] - - -- Styles are transformed then stored in their own node - -- and accTr is push down the tree. - fromDTree' accTr (Node (DStyle s) ts) - = Node (RStyle (transform accTr s)) (fmap (fromDTree' accTr) ts) - - -- Transformations are accumulated and pushed down as well. - fromDTree' accTr (Node (DTransform tr) ts) - = Node REmpty (fmap (fromDTree' (accTr <> tr)) ts) - - fromDTree' accTr (Node (DAnnot a) ts) - = Node (RAnnot a) (fmap (fromDTree' accTr) ts) - - -- Drop accumulated transformations upon encountering a DDelay - -- node --- the tree unfolded beneath it already took into account - -- any transformation at this point. - fromDTree' _ (Node DDelay ts) - = Node REmpty (fmap (fromDTree' mempty) ts) - - -- DEmpty nodes become REmpties, again accTr flows through. - fromDTree' accTr (Node _ ts) - = Node REmpty (fmap (fromDTree' accTr) ts) - --- | Compile a @QDiagram@ into an 'RTree', rewriting styles with the --- given function along the way. Suitable for use by backends when --- implementing 'renderData'. The first argument is the --- transformation used to convert the diagram from local to output --- units. -toRTree - :: (HasLinearMap v, Metric v, Typeable1 v, Typeable n, - OrderedField n, Monoid m, Semigroup m) - => Transformation v n -> QDiagram b v n m -> RTree b v n Annotation -toRTree globalToOutput d - = (fmap . onRStyle) (unmeasureAttrs gToO nToO) - . fromDTree - . fromMaybe (Node DEmpty []) - . toDTree gToO nToO - $ d + lF d = \case + PrimLeaf p -> + let (tr, sty) = untangle d + in primF (unmeasureAttrs g n sty) (transform tr p) + DelayedLeaf f -> + let (QD dia) = f d g n + in foldDUAL lF aF dia + +foldDiaWithScales' + :: (HasLinearMap v, Metric v, OrderedField n, Typeable n, Monoid' m, M.Monoid r) + => (Style v n -> Prim b v n -> r) + -> (Annotation b v n -> r -> r) + -> (Style v n -> r -> r) + -> n + -> n + -> QDiagram b v n m -- ^ diagram to fold + -> r +foldDiaWithScales' primF aF styF g n (QD dual) = foldDUAL' lF aF mkP styF dual where - gToO = avgScale globalToOutput - - -- Scaling factor from normalized units to output units: nth root - -- of product of diameters along each basis direction. Note at - -- this point the diagram has already had the globalToOutput - -- transformation applied, so output = global = local units. - nToO = product (map (`diameter` d) basis) ** (1 / fromIntegral (dimension d)) - --- | Apply a style transformation on 'RStyle' nodes; the identity for --- other 'RNode's. -onRStyle :: (Style v n -> Style v n) -> RNode b v n a -> RNode b v n a -onRStyle f (RStyle s) = RStyle (f s) -onRStyle _ n = n + lF d = \case + PrimLeaf p -> + let (tr, sty) = untangle d + in primF (unmeasureAttrs g n sty) (transform tr p) + DelayedLeaf f -> + let (QD dia) = f d g n + in foldDUAL' lF aF mkP styF dia + + -- The partial sty needs the total transform accumilated so far, but + -- ignores any style before. + mkP d w = transform t (unmeasureAttrs g n sty) + where t = killR d + (_, sty) = untangle w + +-- | Simple way to fold a diagram into a monoidal result. +foldDia + :: (HasLinearMap v, Metric v, OrderedField n, Typeable n, Monoid' m, M.Monoid r) + => (Style v n -> Prim b v n -> r) -- ^ Fold a prim + -> (Annotation b v n -> r -> r) -- ^ Apply an annotation + -> Transformation v n -- ^ final transform for diagram + -> QDiagram b v n m -- ^ diagram to fold + -> r +foldDia primF annF t d = foldDiaWithScales primF annF g n d + where + g = avgScale t + n = normalizedFactor (size d) + +-- | Fold a diagram into a monoidal result. Similar to 'foldDia' but +-- gives access to the style when it's higher up the tree. This is +-- useful for things like clipping where you want to use the same +-- clipping for everything below that point. This is reset after each +-- group and given as the second argument in the prim rendering +-- function. +foldDia' + :: (HasLinearMap v, Metric v, OrderedField n, Typeable n, Monoid' m, M.Monoid r) + => (Style v n -> Prim b v n -> r) + -> (Annotation b v n -> r -> r) + -> (Style v n -> r -> r) + -> Transformation v n + -> QDiagram b v n m -- ^ diagram to fold + -> r +foldDia' primF annF styF t d = foldDiaWithScales' primF annF styF g n d + where + g = avgScale t + n = normalizedFactor (size d) --------------------------------------------------- +-- | Get the normalized scale factor from a vector. For the +-- 'normalizedFactor' of a diagram use this with the 'size' of the +-- diagram. +-- +-- Note: The 'global' factor is the 'avgScale' of the output +-- transform. +normalizedFactor :: (F.Foldable v, Floating n) => v n -> n +normalizedFactor v = F.product v ** (1 / fromIntegral (lengthOf folded v)) -- | Render a diagram, returning also the transformation which was -- used to convert the diagram from its (\"global\") coordinate @@ -186,7 +142,7 @@ renderDiaT :: (Backend b v n , HasLinearMap v, Metric v, Typeable1 v, Typeable n, OrderedField n, Monoid' m) => b -> Options b v n -> QDiagram b v n m -> (Transformation v n, Result b v n) -renderDiaT b opts d = (g2o, renderRTree b opts' . toRTree g2o $ d') +renderDiaT b opts d = (g2o, renderDUAL b opts' g2o d') where (opts', g2o, d') = adjustDia b opts d -- | Render a diagram. diff --git a/src/Diagrams/Core/Envelope.hs b/src/Diagrams/Core/Envelope.hs index 0afeb2a..e3d27fc 100644 --- a/src/Diagrams/Core/Envelope.hs +++ b/src/Diagrams/Core/Envelope.hs @@ -110,6 +110,9 @@ import Linear.Vector -- . See also Brent Yorgey, /Monoids: Theme and Variations/, published in the 2012 Haskell Symposium: ; video: . newtype Envelope v n = Envelope (Option (v n -> Max n)) +type instance V (Envelope v n) = v +type instance N (Envelope v n) = n + instance Wrapped (Envelope v n) where type Unwrapped (Envelope v n) = Option (v n -> Max n) _Wrapped' = iso (\(Envelope e) -> e) Envelope @@ -126,26 +129,22 @@ mkEnvelope :: (v n -> n) -> Envelope v n mkEnvelope = Envelope . Option . Just . (Max .) -- | Create an envelope for the given point. -pointEnvelope :: (Fractional n, Metric v) => Point v n -> Envelope v n +pointEnvelope :: (Metric v, Fractional n) => Point v n -> Envelope v n pointEnvelope p = moveTo p (mkEnvelope $ const 0) -- | Envelopes form a semigroup with pointwise maximum as composition. --- Hence, if @e1@ is the envelope for diagram @d1@, and --- @e2@ is the envelope for @d2@, then @e1 \`mappend\` e2@ --- is the envelope for @d1 \`atop\` d2@. +-- Hence, if @e1@ is the envelope for diagram @d1@, and @e2@ is the +-- envelope for @d2@, then @e1 \`mappend\` e2@ is the envelope for @d1 +-- \`atop\` d2@. deriving instance Ord n => Semigroup (Envelope v n) --- | The special empty envelope is the identity for the --- 'Monoid' instance. +-- | The special empty envelope is the identity for the 'Monoid' +-- instance. deriving instance Ord n => Monoid (Envelope v n) - -- XXX add some diagrams here to illustrate! Note that Haddock supports -- inline images, using a \<\\> syntax. -type instance V (Envelope v n) = v -type instance N (Envelope v n) = n - -- | The local origin of an envelope is the point with respect to -- which bounding queries are made, /i.e./ the point from which the -- input vectors are taken to originate. @@ -192,13 +191,13 @@ class (Metric (V a), OrderedField (N a)) => Enveloped a where instance (Metric v, OrderedField n) => Enveloped (Envelope v n) where getEnvelope = id -instance (OrderedField n, Metric v) => Enveloped (Point v n) where +instance (Metric v, OrderedField n) => Enveloped (Point v n) where getEnvelope p = moveTo p . mkEnvelope $ const 0 instance Enveloped t => Enveloped (TransInv t) where getEnvelope = getEnvelope . op TransInv -instance (Enveloped a, Enveloped b, V a ~ V b, N a ~ N b) => Enveloped (a,b) where +instance (SameSpace a b, Enveloped a, Enveloped b) => Enveloped (a,b) where getEnvelope (x,y) = getEnvelope x <> getEnvelope y instance Enveloped b => Enveloped [b] where @@ -228,12 +227,12 @@ envelopeV v = fromMaybe zero . envelopeVMay v -- | Compute the point on a separating hyperplane in the given -- direction, or @Nothing@ for the empty envelope. -envelopePMay :: (V a ~ v, N a ~ n, Enveloped a) => v n -> a -> Maybe (Point v n) +envelopePMay :: (InSpace v n a, Enveloped a) => v n -> a -> Maybe (Point v n) envelopePMay v = fmap P . envelopeVMay v -- | Compute the point on a separating hyperplane in the given -- direction. Returns the origin for the empty envelope. -envelopeP :: (V a ~ v, N a ~ n, Enveloped a) => v n -> a -> Point v n +envelopeP :: (InSpace v n a, Enveloped a) => v n -> a -> Point v n envelopeP v = P . envelopeV v -- | Equivalent to the norm of 'envelopeVMay': @@ -245,7 +244,7 @@ envelopeP v = P . envelopeV v -- Note that the 'envelopeVMay' / 'envelopePMay' functions above should be -- preferred, as this requires a call to norm. However, it is more -- efficient than calling norm on the results of those functions. -envelopeSMay :: (V a ~ v, N a ~ n, Enveloped a) => v n -> a -> Maybe n +envelopeSMay :: (InSpace v n a, Enveloped a) => v n -> a -> Maybe n envelopeSMay v = fmap ((* norm v) . ($ v)) . appEnvelope . getEnvelope -- | Equivalent to the norm of 'envelopeV': @@ -257,26 +256,26 @@ envelopeSMay v = fmap ((* norm v) . ($ v)) . appEnvelope . getEnvelope -- Note that the 'envelopeV' / 'envelopeP' functions above should be -- preferred, as this requires a call to norm. However, it is more -- efficient than calling norm on the results of those functions. -envelopeS :: (V a ~ v, N a ~ n, Enveloped a, Num n) => v n -> a -> n +envelopeS :: (InSpace v n a, Enveloped a, Num n) => v n -> a -> n envelopeS v = fromMaybe 0 . envelopeSMay v -- | Compute the diameter of a enveloped object along a particular -- vector. Returns zero for the empty envelope. -diameter :: (V a ~ v, N a ~ n, Enveloped a) => v n -> a -> n +diameter :: (InSpace v n a, Enveloped a) => v n -> a -> n diameter v a = maybe 0 (\(lo,hi) -> (hi - lo) * norm v) (extent v a) -- | Compute the \"radius\" (1\/2 the diameter) of an enveloped object -- along a particular vector. -radius :: (V a ~ v, N a ~ n, Enveloped a) => v n -> a -> n +radius :: (InSpace v n a, Enveloped a) => v n -> a -> n radius v = (0.5*) . diameter v -- | Compute the range of an enveloped object along a certain -- direction. Returns a pair of scalars @(lo,hi)@ such that the -- object extends from @(lo *^ v)@ to @(hi *^ v)@. Returns @Nothing@ -- for objects with an empty envelope. -extent :: (V a ~ v, N a ~ n, Enveloped a) => v n -> a -> Maybe (n, n) +extent :: (InSpace v n a, Enveloped a) => v n -> a -> Maybe (n, n) extent v a = (\f -> (-f (negated v), f v)) <$> (appEnvelope . getEnvelope $ a) -- | The smallest positive vector that bounds the envelope of an object. -size :: (V a ~ v, N a ~ n, Enveloped a, HasBasis v) => a -> v n +size :: (InSpace v n a, HasBasis v, Enveloped a) => a -> v n size d = tabulate $ \(E l) -> diameter (zero & l .~ 1) d diff --git a/src/Diagrams/Core/Query.hs b/src/Diagrams/Core/Query.hs index 7c83a05..18936aa 100644 --- a/src/Diagrams/Core/Query.hs +++ b/src/Diagrams/Core/Query.hs @@ -17,11 +17,14 @@ module Diagrams.Core.Query ( Query (..) + , queryPoint ) where import Control.Applicative -import Control.Lens (Rewrapped, Wrapped (..), iso) +import Control.Lens import Data.Semigroup +import Data.Distributive +import Data.Functor.Rep import Linear.Affine import Linear.Vector @@ -43,6 +46,22 @@ import Diagrams.Core.V newtype Query v n m = Query { runQuery :: Point v n -> m } deriving (Functor, Applicative, Semigroup, Monoid) +instance Distributive (Query v n) where + distribute a = Query $ \p -> fmap (\(Query q) -> q p) a + +instance Representable (Query v n) where + type Rep (Query v n) = Point v n + tabulate = Query + index = runQuery + +instance Functor v => Profunctor (Query v) where + lmap f (Query q) = Query $ \p -> q (fmap f p) + rmap = fmap + +-- | Setter over the input point of a query. +queryPoint :: Setter (Query v' n' m) (Query v n m) (Point v n) (Point v' n') +queryPoint = sets $ \f (Query q) -> Query $ q . f + instance Wrapped (Query v n m) where type Unwrapped (Query v n m) = Point v n -> m _Wrapped' = iso runQuery Query @@ -53,7 +72,7 @@ type instance V (Query v n m) = v type instance N (Query v n m) = n instance (Additive v, Num n) => HasOrigin (Query v n m) where - moveOriginTo (P u) (Query f) = Query $ \p -> f (p .+^ u) + moveOriginTo (P u) = queryPoint %~ (.+^ u) instance (Additive v, Num n) => Transformable (Query v n m) where - transform t (Query f) = Query $ f . papply (inv t) + transform t = queryPoint %~ papply (inv t) diff --git a/src/Diagrams/Core/Style.hs b/src/Diagrams/Core/Style.hs index a30b3e7..efb01aa 100644 --- a/src/Diagrams/Core/Style.hs +++ b/src/Diagrams/Core/Style.hs @@ -1,4 +1,5 @@ {-# LANGUAGE DataKinds #-} +{-# LANGUAGE DefaultSignatures #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE MultiParamTypeClasses #-} @@ -73,6 +74,13 @@ import qualified Data.Map as M import Data.Monoid.Action as A import Data.Semigroup import qualified Data.Set as S +import qualified Data.HashSet as HS +import Data.Tree (Tree) +import Data.HashMap.Lazy (HashMap) +import Data.Sequence (Seq) +import Data.Map (Map) +import Data.IntMap (IntMap) +import Data.Hashable (Hashable) import Data.Typeable import Diagrams.Core.Measure @@ -329,27 +337,35 @@ class HasStyle a where -- existing style. applyStyle :: Style (V a) (N a) -> a -> a + default applyStyle :: Functor f => Style (V a) (N a) -> f a -> f a + applyStyle = fmap . applyStyle + instance Typeable n => HasStyle (Style v n) where applyStyle = mappend -instance (HasStyle a, HasStyle b, V a ~ V b, N a ~ N b) => HasStyle (a,b) where - applyStyle s = applyStyle s *** applyStyle s - -instance HasStyle a => HasStyle [a] where - applyStyle = fmap . applyStyle +instance (SameSpace a b, HasStyle a, HasStyle b) + => HasStyle (a,b) where + applyStyle s (a,b) = (applyStyle s a, applyStyle s b) -instance HasStyle b => HasStyle (a -> b) where - applyStyle = fmap . applyStyle - -instance HasStyle a => HasStyle (M.Map k a) where - applyStyle = fmap . applyStyle +instance (SameSpace a b, SameSpace b c, HasStyle a, HasStyle b, HasStyle c) + => HasStyle (a,b,c) where + applyStyle s (a,b,c) = (applyStyle s a, applyStyle s b, applyStyle s c) instance (HasStyle a, Ord a) => HasStyle (S.Set a) where applyStyle = S.map . applyStyle -instance HasStyle b => HasStyle (Measured n b) where - applyStyle = fmap . applyStyle +instance (HasStyle a, Hashable a, Eq a) => HasStyle (HS.HashSet a) where + applyStyle = HS.map . applyStyle +instance HasStyle a => HasStyle [a] +instance HasStyle a => HasStyle (Seq a) +instance HasStyle a => HasStyle (Tree a) +instance HasStyle a => HasStyle (Maybe a) +instance HasStyle a => HasStyle (Map k a) +instance HasStyle a => HasStyle (IntMap a) +instance HasStyle a => HasStyle (HashMap k a) + +instance HasStyle b => HasStyle (Measured n b) where -- | Apply an attribute to an instance of 'HasStyle' (such as a -- diagram or a style). If the object already has an attribute of -- the same type, the new attribute is combined on the left with the diff --git a/src/Diagrams/Core/Transform.hs b/src/Diagrams/Core/Transform.hs index cc8d1c4..fb2a617 100644 --- a/src/Diagrams/Core/Transform.hs +++ b/src/Diagrams/Core/Transform.hs @@ -7,6 +7,7 @@ {-# LANGUAGE TypeOperators #-} {-# LANGUAGE TypeSynonymInstances #-} {-# LANGUAGE UndecidableInstances #-} +{-# LANGUAGE DefaultSignatures #-} {-# OPTIONS_GHC -fno-warn-unused-imports #-} ----------------------------------------------------------------------------- -- | @@ -73,9 +74,15 @@ module Diagrams.Core.Transform import Control.Lens (Rewrapped, Traversable, Wrapped (..), iso, (&), (.~)) -import qualified Data.Map as M +import Data.IntMap (IntMap) +import Data.Map (Map) import Data.Semigroup import qualified Data.Set as S +import qualified Data.HashSet as HS +import Data.Tree (Tree) +import Data.HashMap.Lazy (HashMap) +import Data.Sequence (Seq) +import Data.Hashable (Hashable) import Data.Monoid.Action import Data.Monoid.Deletable @@ -317,10 +324,13 @@ class (Additive v, Representable v, Rep v ~ E v) => HasBasis v instance (Additive v, Representable v, Rep v ~ E v) => HasBasis v -- | Type class for things @t@ which can be transformed. -class Transformable t where +class Transformable a where -- | Apply a transformation to an object. - transform :: Transformation (V t) (N t) -> t -> t + transform :: Transformation (V a) (N a) -> a -> a + + default transform :: Functor f => Transformation (V a) (N a) -> f a -> f a + transform = fmap . transform instance (Additive v, Num n) => Transformable (Transformation v n) where transform t1 t2 = t1 <> t2 @@ -328,18 +338,18 @@ instance (Additive v, Num n) => Transformable (Transformation v n) where instance (Additive v, Num n) => HasOrigin (Transformation v n) where moveOriginTo p = translate (origin .-. p) -instance (Transformable t, Transformable s, V t ~ V s, N t ~ N s) - => Transformable (t, s) where - transform t (x,y) = ( transform t x - , transform t y - ) +instance (SameSpace a b, Transformable a, Transformable b) + => Transformable (a, b) where + transform t (x,y) = (transform t x , transform t y) + +instance (SameSpace a b, SameSpace b c, Transformable a, Transformable b, Transformable c) + => Transformable (a,b,c) where + transform t (a,b,c) = (transform t a, transform t b, transform t c) -instance (Transformable t, Transformable s, Transformable u, V s ~ V t, N s ~ N t, V s ~ V u, N s ~ N u) - => Transformable (t,s,u) where - transform t (x,y,z) = ( transform t x - , transform t y - , transform t z - ) +instance (SameSpace a b, SameSpace b c, SameSpace c d, + Transformable a, Transformable b, Transformable c, Transformable d) + => Transformable (a,b,c,d) where + transform t (a,b,c,d) = (transform t a, transform t b, transform t c, transform t d) -- Transform functions by conjugation. That is, reverse-transform argument and -- forward-transform result. Intuition: If someone shrinks you, you see your @@ -347,26 +357,27 @@ instance (Transformable t, Transformable s, Transformable u, V s ~ V t, N s ~ N -- rotating left. Etc. This technique was used extensively in Pan for modular -- construction of image filters. Works well for curried functions, since all -- arguments get inversely transformed. - -instance ( V t ~ v, N t ~ n, V t ~ V s, N t ~ N s, Functor v, Num n - , Transformable t, Transformable s) - => Transformable (s -> t) where - transform tr f = transform tr . f . transform (inv tr) - -instance Transformable t => Transformable [t] where - transform = map . transform - -instance (Transformable t, Ord t) => Transformable (S.Set t) where - transform = S.map . transform - -instance Transformable t => Transformable (M.Map k t) where - transform = M.map . transform +instance (SameSpace a b, Functor (V b), Num (N b), Transformable a, Transformable b) + => Transformable (a -> b) where + transform t f = transform t . f . transform (inv t) instance (Additive v, Num n) => Transformable (Point v n) where transform = papply -instance Transformable m => Transformable (Deletable m) where - transform = fmap . transform +instance (Transformable a, Ord a) => Transformable (S.Set a) where + transform = S.map . transform + +instance (Transformable a, Hashable a, Eq a) => Transformable (HS.HashSet a) where + transform = HS.map . transform + +instance Transformable a => Transformable [a] +instance Transformable a => Transformable (Seq a) +instance Transformable a => Transformable (Tree a) +instance Transformable a => Transformable (Maybe a) +instance Transformable a => Transformable (Map k a) +instance Transformable a => Transformable (IntMap a) +instance Transformable a => Transformable (HashMap k a) +instance Transformable m => Transformable (Deletable m) ------------------------------------------------------------ -- Translational invariance ------------------------------ diff --git a/src/Diagrams/Core/Types.hs b/src/Diagrams/Core/Types.hs index e29a334..0bc0c76 100644 --- a/src/Diagrams/Core/Types.hs +++ b/src/Diagrams/Core/Types.hs @@ -1,16 +1,18 @@ -{-# LANGUAGE CPP #-} -{-# LANGUAGE DeriveDataTypeable #-} -{-# LANGUAGE DeriveFunctor #-} -{-# LANGUAGE EmptyDataDecls #-} -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE GADTs #-} -{-# LANGUAGE MultiParamTypeClasses #-} -{-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE TupleSections #-} -{-# LANGUAGE TypeFamilies #-} -{-# LANGUAGE TypeOperators #-} -{-# LANGUAGE UndecidableInstances #-} +{-# LANGUAGE CPP #-} +{-# LANGUAGE DeriveDataTypeable #-} +{-# LANGUAGE DeriveFunctor #-} +{-# LANGUAGE EmptyDataDecls #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE GeneralizedNewtypeDeriving #-} +{-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE StandaloneDeriving #-} +{-# LANGUAGE TupleSections #-} +{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE TypeOperators #-} +{-# LANGUAGE UndecidableInstances #-} {-# OPTIONS_GHC -fno-warn-orphans #-} -- We have some orphan Action instances here, but since Action is a multi-param @@ -50,11 +52,12 @@ module Diagrams.Core.Types , applyAnnotation, href, opacityGroup, groupOpacity -- *** Dynamic (monoidal) annotations - , UpAnnots, DownAnnots, transfToAnnot, transfFromAnnot + , UpAnnots, DownAnnots, downT, transfFromAnnot -- ** Basic type definitions - , QDiaLeaf(..), withQDiaLeaf - , QDiagram(..), Diagram + , QDiaLeaf(..) + , QDiagram(..) + , Diagram -- * Operations on diagrams -- ** Creating diagrams @@ -107,15 +110,6 @@ module Diagrams.Core.Types -- * Backends , Backend(..) - , DTree - , DNode(..) - - , RTree - , RNode(..) - , _RStyle - , _RAnnot - , _RPrim - , _REmpty -- ** Null backend @@ -130,26 +124,20 @@ module Diagrams.Core.Types ) where -import Control.Arrow (first, second, (***)) -import Control.Lens (Lens', Prism', Rewrapped, - Wrapped (..), iso, lens, over, - prism', view, (^.), _Wrapped, - _Wrapping) +import Control.Arrow (second, (***)) +import Control.Lens hiding (transform) import Control.Monad (mplus) import Data.List (isSuffixOf) import qualified Data.Map as M import Data.Maybe (fromMaybe, listToMaybe) import Data.Semigroup import qualified Data.Traversable as T -import Data.Tree import Data.Typeable import Data.Monoid.Action import Data.Monoid.Coproduct -import Data.Monoid.Deletable -import Data.Monoid.MList import Data.Monoid.WithSemigroup -import qualified Data.Tree.DUAL as D +import qualified Data.Tree.DUAL.Internal as D import Diagrams.Core.Envelope import Diagrams.Core.HasOrigin @@ -175,9 +163,11 @@ class (Typeable n, RealFloat n) => TypeableFloat n instance (Typeable n, RealFloat n) => TypeableFloat n -- use class instead of type constraint so users don't need constraint kinds pragma ------------------------------------------------------------- --- Diagrams ---------------------------------------------- ------------------------------------------------------------- +------------------------------------------------------------------------ +-- Diagrams +------------------------------------------------------------------------ + +-- Anotations ---------------------------------------------------------- -- | Monoidal annotations which travel up the diagram tree, /i.e./ which -- are aggregated from component diagrams to the whole: @@ -194,38 +184,87 @@ instance (Typeable n, RealFloat n) => TypeableFloat n -- * name/subdiagram associations (see "Diagrams.Core.Names") -- -- * query functions (see "Diagrams.Core.Query") -type UpAnnots b v n m = Deletable (Envelope v n) - ::: Deletable (Trace v n) - ::: Deletable (SubMap b v n m) - ::: Query v n m - ::: () +newtype UpAnnots b v n m = UpAnnots (Envelope v n, Trace v n, SubMap b v n m, Query v n m) + deriving (Semigroup, Monoid, Functor) + +type instance V (UpAnnots b v n m) = v +type instance N (UpAnnots b v n m) = n + +instance r ~ UpAnnots b' v' n' m' => Rewrapped (UpAnnots b v n m) r +instance Wrapped (UpAnnots b v n m) where + type Unwrapped (UpAnnots b v n m) = (Envelope v n, Trace v n, SubMap b v n m, Query v n m) + _Wrapped' = iso (\(UpAnnots a) -> a) UpAnnots + +instance (Metric v, OrderedField n) => Transformable (UpAnnots b v n m) where + transform = over _Wrapped . transform + +-- | Affine traversal over the top level upwards annotations. Does +-- nothing for empty diagram. +upAnnots :: Traversal' (QDiagram b v n m) (UpAnnots b v n m) +upAnnots = _Wrapped' . D._u + +-- | Traversal over the envelope of a diagram. Does nothing for the +-- empty diagram. +envelope :: Traversal' (QDiagram b v n m) (Envelope v n) +envelope = upAnnots . _Wrapped' . _1 + +-- | Traversal over the trace of a diagram. Does nothing for the +-- empty diagram. +trace :: Traversal' (QDiagram b v n m) (Trace v n) +trace = upAnnots . _Wrapped' . _2 + +-- | Traversal over the 'Subdiagram' mapping of a diagram. Does nothing +-- for the empty diagram. +subMap :: Traversal' (QDiagram b v n m) (SubMap b v n m) +subMap = upAnnots . _Wrapped' . _3 + +-- | Traversal over the query of a diagram. Does nothing for the +-- empty diagram. +query :: Traversal' (QDiagram b v n m) (Query v n m) +query = upAnnots . _Wrapped' . _4 + +-- are these still needed? + +-- | Replace the envelope of a diagram. Note this is different from +-- @'set' 'envelope'@ because it will set the envelope for the empty +-- diagram. +setEnvelope :: (OrderedField n, Metric v, Monoid' m) + => Envelope v n -> QDiagram b v n m -> QDiagram b v n m +setEnvelope e (QD D.EmptyDUAL) = QD $ D.leafU (mempty & _Wrapped' . _1 .~ e) +setEnvelope e dia = set envelope e dia + +-- | Replace the envelope of a diagram. Note this is different from +-- @'set' 'trace@ because it will set the trace for the empty +-- diagram. +setTrace :: (OrderedField n, Metric v, Monoid' m) + => Trace v n -> QDiagram b v n m -> QDiagram b v n m +setTrace t (QD D.EmptyDUAL) = QD $ D.leafU (mempty & _Wrapped' . _2 .~ t) +setTrace t dia = set trace t dia -- | Monoidal annotations which travel down the diagram tree, -- /i.e./ which accumulate along each path to a leaf (and which can -- act on the upwards-travelling annotations): -- -- * styles (see "Diagrams.Core.Style") --- --- * names (see "Diagrams.Core.Names") -type DownAnnots v n = (Transformation v n :+: Style v n) - ::: Name - ::: () +type DownAnnots v n = Transformation v n :+: Style v n - -- Note that we have to put the transformations and styles together - -- using a coproduct because the transformations can act on the - -- styles. + -- Note that we put the transformations and styles together using a + -- coproduct because the transformations can act on the styles. --- | Inject a transformation into a default downwards annotation --- value. -transfToAnnot :: Transformation v n -> DownAnnots v n -transfToAnnot - = inj - . (inL :: Transformation v n -> Transformation v n :+: Style v n) +-- | Make a downwards annotation from a transform. +downT :: Transformation v n -> DownAnnots v n +downT = inL + +-- | Make a downwards annotation from a style. +downSty :: Style v n -> DownAnnots v n +downSty = inR -- | Extract the (total) transformation from a downwards annotation -- value. transfFromAnnot :: (Additive v, Num n) => DownAnnots v n -> Transformation v n -transfFromAnnot = option mempty killR . fst +transfFromAnnot = killR + +-- Leafs --------------------------------------------------------------- -- | A leaf in a 'QDiagram' tree is either a 'Prim', or a \"delayed\" -- @QDiagram@ which expands to a real @QDiagram@ once it learns the @@ -242,23 +281,25 @@ data QDiaLeaf b v n m -- be applied by the context). deriving Functor -withQDiaLeaf :: (Prim b v n -> r) - -> ((DownAnnots v n -> n -> n -> QDiagram b v n m) -> r) - -> QDiaLeaf b v n m -> r -withQDiaLeaf f _ (PrimLeaf p) = f p -withQDiaLeaf _ g (DelayedLeaf dgn) = g dgn +-- Static annotation --------------------------------------------------- -- | Static annotations which can be placed at a particular node of a -- diagram tree. -data Annotation +data Annotation b (v :: * -> *) n = Href String -- ^ Hyperlink | OpacityGroup Double deriving Show +type instance V (Annotation b v n) = v +type instance N (Annotation b v n) = n + +instance Transformable (Annotation b v n) where + transform _ = id + -- | Apply a static annotation at the root of a diagram. applyAnnotation :: (Metric v, OrderedField n, Semigroup m) - => Annotation -> QDiagram b v n m -> QDiagram b v n m + => Annotation b v n -> QDiagram b v n m -> QDiagram b v n m applyAnnotation an (QD dt) = QD (D.annot an dt) -- | Make a diagram into a hyperlink. Note that only some backends @@ -273,6 +314,7 @@ opacityGroup, groupOpacity :: (Metric v, OrderedField n, Semigroup m) opacityGroup = applyAnnotation . OpacityGroup groupOpacity = applyAnnotation . OpacityGroup +-- QDiagram ------------------------------------------------------------ -- | The fundamental diagram type. The type variables are as follows: -- @@ -305,7 +347,7 @@ groupOpacity = applyAnnotation . OpacityGroup -- is not really a very good name, but it's probably not worth -- changing it at this point. newtype QDiagram b v n m - = QD (D.DUALTree (DownAnnots v n) (UpAnnots b v n m) Annotation (QDiaLeaf b v n m)) + = QD (D.DUALTree (DownAnnots v n) (UpAnnots b v n m) (Annotation b v n) (QDiaLeaf b v n m)) #if __GLASGOW_HASKELL__ >= 707 deriving Typeable #else @@ -318,7 +360,7 @@ instance forall b v. (Typeable b, Typeable1 v) => Typeable2 (QDiagram b v) where instance Wrapped (QDiagram b v n m) where type Unwrapped (QDiagram b v n m) = - D.DUALTree (DownAnnots v n) (UpAnnots b v n m) Annotation (QDiaLeaf b v n m) + D.DUALTree (DownAnnots v n) (UpAnnots b v n m) (Annotation b v n) (QDiaLeaf b v n m) _Wrapped' = iso (\(QD d) -> d) QD instance Rewrapped (QDiagram b v n m) (QDiagram b' v' n' m') @@ -336,53 +378,10 @@ type Diagram b = QDiagram b (V b) (N b) Any -- | Create a \"point diagram\", which has no content, no trace, an -- empty query, and a point envelope. -pointDiagram :: (Metric v, Fractional n) - => Point v n -> QDiagram b v n m -pointDiagram p = QD $ D.leafU (inj . toDeletable $ pointEnvelope p) - --- | A useful variant of 'getU' which projects out a certain --- component. -getU' :: (Monoid u', u :>: u') => D.DUALTree d u a l -> u' -getU' = maybe mempty (option mempty id . get) . D.getU - --- | Lens onto the 'Envelope' of a 'QDiagram'. -envelope :: (OrderedField n, Metric v, Monoid' m) - => Lens' (QDiagram b v n m) (Envelope v n) -envelope = lens (unDelete . getU' . view _Wrapped') (flip setEnvelope) - --- | Replace the envelope of a diagram. -setEnvelope :: forall b v n m. ( OrderedField n, Metric v - , Monoid' m) - => Envelope v n -> QDiagram b v n m -> QDiagram b v n m -setEnvelope e = - over _Wrapped' ( D.applyUpre (inj . toDeletable $ e) - . D.applyUpre (inj (deleteL :: Deletable (Envelope v n))) - . D.applyUpost (inj (deleteR :: Deletable (Envelope v n))) - ) - --- | Lens onto the 'Trace' of a 'QDiagram'. -trace :: (Metric v, OrderedField n, Semigroup m) => - Lens' (QDiagram b v n m) (Trace v n) -trace = lens (unDelete . getU' . view _Wrapped') (flip setTrace) - --- | Replace the trace of a diagram. -setTrace :: forall b v n m. ( OrderedField n, Metric v - , Semigroup m) - => Trace v n -> QDiagram b v n m -> QDiagram b v n m -setTrace t = over _Wrapped' ( D.applyUpre (inj . toDeletable $ t) - . D.applyUpre (inj (deleteL :: Deletable (Trace v n))) - . D.applyUpost (inj (deleteR :: Deletable (Trace v n))) - ) - --- | Lens onto the 'SubMap' of a 'QDiagram' (/i.e./ an association from --- names to subdiagrams). -subMap :: (Metric v, Semigroup m, OrderedField n) - => Lens' (QDiagram b v n m) (SubMap b v n m) -subMap = lens (unDelete . getU' . view _Wrapped') (flip setMap) - where - setMap :: (Metric v, Semigroup m, OrderedField n) => - SubMap b v n m -> QDiagram b v n m -> QDiagram b v n m - setMap m = over _Wrapped' ( D.applyUpre . inj . toDeletable $ m) +pointDiagram :: (Metric v, OrderedField n, Monoid m) => Point v n -> QDiagram b v n m +pointDiagram p = QD $ D.leafU (mempty & _Wrapped . _1 .~ pointEnvelope p) + +-- Names --------------------------------------------------------------- -- | Get a list of names of subdiagrams and their locations. names :: (Metric v, Semigroup m, OrderedField n) @@ -397,7 +396,7 @@ names = (map . second . map) location . M.assocs . view (subMap . _Wrapped') nameSub :: (IsName nm , Metric v, OrderedField n, Semigroup m) => (QDiagram b v n m -> Subdiagram b v n m) -> nm -> QDiagram b v n m -> QDiagram b v n m nameSub s n d = d' - where d' = over _Wrapped' (D.applyUpre . inj . toDeletable $ fromNames [(n,s d')]) d + where d' = over subMap (fromNames [(n,s d')] <>) d -- | Lookup the most recent diagram associated with (some -- qualification of) the given name. @@ -430,8 +429,7 @@ withNameAll n f d = f (fromMaybe [] (lookupSub (toName n) (d^.subMap))) d -- list of most recent subdiagrams associated with (some qualification -- of) each name. Do nothing (the identity transformation) if any -- of the names do not exist. -withNames :: (IsName nm, Metric v - , Semigroup m, OrderedField n) +withNames :: (IsName nm, Metric v, Semigroup m, OrderedField n) => [nm] -> ([Subdiagram b v n m] -> QDiagram b v n m -> QDiagram b v n m) -> QDiagram b v n m -> QDiagram b v n m withNames ns f d = maybe id f ns' d @@ -441,19 +439,13 @@ withNames ns f d = maybe id f ns' d -- | \"Localize\" a diagram by hiding all the names, so they are no -- longer visible to the outside. -localize :: forall b v n m. (Metric v, OrderedField n, Semigroup m) +localize :: (Metric v, OrderedField n, Semigroup m) => QDiagram b v n m -> QDiagram b v n m -localize = over _Wrapped' ( D.applyUpre (inj (deleteL :: Deletable (SubMap b v n m))) - . D.applyUpost (inj (deleteR :: Deletable (SubMap b v n m))) - ) - --- | Get the query function associated with a diagram. -query :: Monoid m => QDiagram b v n m -> Query v n m -query = getU' . view _Wrapped' +localize = set subMap mempty -- | Sample a diagram's query function at a given point. sample :: Monoid m => QDiagram b v n m -> Point v n -> m -sample = runQuery . query +sample = runQuery . view query -- | Set the query value for 'True' points in a diagram (/i.e./ points -- \"inside\" the diagram); 'False' points will be set to 'mempty'. @@ -484,14 +476,26 @@ mkQD p = mkQD' (PrimLeaf p) -- trace, subdiagram map, and query function. mkQD' :: QDiaLeaf b v n m -> Envelope v n -> Trace v n -> SubMap b v n m -> Query v n m -> QDiagram b v n m -mkQD' l e t n q - = QD $ D.leaf (toDeletable e *: toDeletable t *: toDeletable n *: q *: ()) l +mkQD' l e t n q = QD $ D.leaf (UpAnnots (e,t,n,q)) l + +-- should this be in Diagrams.Combinators? + +-- | A convenient synonym for 'mappend' on diagrams, designed to be +-- used infix (to help remember which diagram goes on top of which +-- when combining them, namely, the first on top of the second). +atop :: (OrderedField n, Metric v, Semigroup m) + => QDiagram b v n m -> QDiagram b v n m -> QDiagram b v n m +atop = (<>) ------------------------------------------------------------- --- Instances ------------------------------------------------------------- +infixl 6 `atop` ----- Monoid +-- Instances ----------------------------------------------------------- + +instance (Metric v, OrderedField n, Semigroup m) + => Semigroup (QDiagram b v n m) where + QD d1 <> QD d2 = QD (d2 <> d1) + -- swap order so that primitives of d2 come first, i.e. will be + -- rendered first, i.e. will be on the bottom. -- | Diagrams form a monoid since each of their components do: the -- empty diagram has no primitives, an empty envelope, an empty @@ -507,34 +511,13 @@ mkQD' l e t n q -- diagrams when viewed by 4-dimensional beings. instance (Metric v, OrderedField n, Semigroup m) => Monoid (QDiagram b v n m) where - mempty = QD D.empty + mempty = QD mempty mappend = (<>) -instance (Metric v, OrderedField n, Semigroup m) - => Semigroup (QDiagram b v n m) where - (QD d1) <> (QD d2) = QD (d2 <> d1) - -- swap order so that primitives of d2 come first, i.e. will be - -- rendered first, i.e. will be on the bottom. - --- | A convenient synonym for 'mappend' on diagrams, designed to be --- used infix (to help remember which diagram goes on top of which --- when combining them, namely, the first on top of the second). -atop :: (OrderedField n, Metric v, Semigroup m) - => QDiagram b v n m -> QDiagram b v n m -> QDiagram b v n m -atop = (<>) - -infixl 6 `atop` - ----- Functor - instance Functor (QDiagram b v n) where - fmap f = over (_Wrapping QD) - ( (D.mapU . second . second) - ( (first . fmap . fmap . fmap) f - . (second . first . fmap . fmap) f - ) - . (fmap . fmap) f - ) + fmap f = over _Wrapped + $ (D._u . mapped %~ f) -- up annots + . (fmap . fmap) f -- leaves ---- Applicative @@ -553,59 +536,42 @@ instance Functor (QDiagram b v n) where -- (Diagram ps1 bs1 ns1 smp1) <*> (Diagram ps2 bs2 ns2 smp2) -- = Diagram (ps1 <> ps2) (bs1 <> bs2) (ns1 <> ns2) (smp1 <*> smp2) ----- HasStyle - -instance (Metric v, OrderedField n, Semigroup m) - => HasStyle (QDiagram b v n m) where - applyStyle = over _Wrapped' . D.applyD . inj - . (inR :: Style v n -> Transformation v n :+: Style v n) - ----- Juxtaposable +instance (Metric v, OrderedField n, Semigroup m) => HasStyle (QDiagram b v n m) where + applyStyle = over _Wrapped' . D.down . downSty instance (Metric v, OrderedField n, Monoid' m) - => Juxtaposable (QDiagram b v n m) where + => Juxtaposable (QDiagram b v n m) where juxtapose = juxtaposeDefault ----- Enveloped - instance (Metric v, OrderedField n, Monoid' m) - => Enveloped (QDiagram b v n m) where + => Enveloped (QDiagram b v n m) where getEnvelope = view envelope ----- Traced - instance (Metric v, OrderedField n, Semigroup m) - => Traced (QDiagram b v n m) where + => Traced (QDiagram b v n m) where getTrace = view trace ----- HasOrigin - -- | Every diagram has an intrinsic \"local origin\" which is the -- basis for all combining operations. instance (Metric v, OrderedField n, Semigroup m) - => HasOrigin (QDiagram b v n m) where + => HasOrigin (QDiagram b v n m) where moveOriginTo = translate . (origin .-.) ----- Transformable - -- | Diagrams can be transformed by transforming each of their -- components appropriately. instance (OrderedField n, Metric v, Semigroup m) - => Transformable (QDiagram b v n m) where - transform = over _Wrapped' . D.applyD . transfToAnnot - ----- Qualifiable + => Transformable (QDiagram b v n m) where + transform = over _Wrapped' . D.down . downT -- | Diagrams can be qualified so that all their named points can -- now be referred to using the qualification prefix. instance (Metric v, OrderedField n, Semigroup m) - => Qualifiable (QDiagram b v n m) where - (.>>) = over _Wrapped' . D.applyD . inj . toName - + => Qualifiable (QDiagram b v n m) where + n .>> d = over subMap (n .>>) d ------------------------------------------------------------- +------------------------------------------------------------------------ -- Subdiagrams ------------------------------------------------------------- +------------------------------------------------------------------------ -- | A @Subdiagram@ represents a diagram embedded within the context -- of a larger diagram. Essentially, it consists of a diagram @@ -619,7 +585,7 @@ type instance N (Subdiagram b v n m) = n -- | Turn a diagram into a subdiagram with no accumulated context. mkSubdiagram :: QDiagram b v n m -> Subdiagram b v n m -mkSubdiagram d = Subdiagram d empty +mkSubdiagram d = Subdiagram d mempty -- | Create a \"point subdiagram\", that is, a 'pointDiagram' (with no -- content and a point envelope) treated as a subdiagram with local @@ -627,11 +593,11 @@ mkSubdiagram d = Subdiagram d empty -- @mkSubdiagram . pointDiagram@, which would result in a subdiagram -- with local origin at the parent origin, rather than at the given -- point. -subPoint :: (Metric v, OrderedField n, Semigroup m) +subPoint :: (Metric v, OrderedField n, Monoid' m) => Point v n -> Subdiagram b v n m subPoint p = Subdiagram (pointDiagram origin) - (transfToAnnot $ translation (p .-. origin)) + (downT $ translation (p .-. origin)) instance Functor (Subdiagram b v n) where fmap f (Subdiagram d a) = Subdiagram (fmap f d) a @@ -650,7 +616,7 @@ instance (Metric v, OrderedField n) instance (Metric v, Floating n) => Transformable (Subdiagram b v n m) where - transform t (Subdiagram d a) = Subdiagram d (transfToAnnot t <> a) + transform t (Subdiagram d a) = Subdiagram d (downT t <> a) -- | Get the location of a subdiagram; that is, the location of its -- local origin /with respect to/ the vector space of its parent @@ -667,16 +633,16 @@ location (Subdiagram _ a) = transform (transfFromAnnot a) origin -- diagram. getSub :: (Metric v, OrderedField n, Semigroup m) => Subdiagram b v n m -> QDiagram b v n m -getSub (Subdiagram d a) = over _Wrapped' (D.applyD a) d +getSub (Subdiagram d a) = over _Wrapped' (D.down a) d -- | Extract the \"raw\" content of a subdiagram, by throwing away the -- context. rawSub :: Subdiagram b v n m -> QDiagram b v n m rawSub (Subdiagram d _) = d ------------------------------------------------------------- --- Subdiagram maps --------------------------------------- ------------------------------------------------------------- +------------------------------------------------------------------------ +-- Subdiagram maps +------------------------------------------------------------------------ -- | A 'SubMap' is a map associating names to subdiagrams. There can -- be multiple associations for any given name. @@ -709,44 +675,37 @@ instance Semigroup (SubMap b v n m) where -- will associate that name to the concatenation of the information -- associated with that name. instance Monoid (SubMap b v n m) where - mempty = SubMap M.empty + mempty = SubMap mempty mappend = (<>) -instance (OrderedField n, Metric v) - => HasOrigin (SubMap b v n m) where +instance (OrderedField n, Metric v) => HasOrigin (SubMap b v n m) where moveOriginTo = over _Wrapped' . moveOriginTo -instance (Metric v, Floating n) - => Transformable (SubMap b v n m) where +instance (Metric v, Floating n) => Transformable (SubMap b v n m) where transform = over _Wrapped' . transform --- | 'SubMap's are qualifiable: if @ns@ is a 'SubMap', then @a |> +-- | 'SubMap's are qualifiable: if @ns@ is a 'SubMap', then @a .>> -- ns@ is the same 'SubMap' except with every name qualified by -- @a@. instance Qualifiable (SubMap b v n m) where - a .>> (SubMap m) = SubMap $ M.mapKeys (a .>>) m + a .>> SubMap m = SubMap $ M.mapKeys (a .>>) m -- | Construct a 'SubMap' from a list of associations between names -- and subdiagrams. fromNames :: IsName a => [(a, Subdiagram b v n m)] -> SubMap b v n m fromNames = SubMap . M.fromListWith (++) . map (toName *** (:[])) --- | Add a name/diagram association to a submap. +-- | Add a name/diagram association to a subMap. rememberAs :: IsName a => a -> QDiagram b v n m -> SubMap b v n m -> SubMap b v n m rememberAs n b = over _Wrapped' $ M.insertWith (++) (toName n) [mkSubdiagram b] --- | A name acts on a name map by qualifying every name in it. +-- | Qualify every name in the 'SubMap'. instance Action Name (SubMap b v n m) where act = (.>>) -instance Action Name a => Action Name (Deletable a) where - act n (Deletable l a r) = Deletable l (act n a) r - --- Names do not act on other things. - -instance Action Name (Query v n m) -instance Action Name (Envelope v n) -instance Action Name (Trace v n) +-- | Qualify every name in the 'SubMap'. +instance Action Name (UpAnnots b v n m) where + act = over (_Wrapped . _3) . act -- | Look for the given name in a name map, returning a list of -- subdiagrams associated with that name. If no names match the @@ -761,9 +720,9 @@ lookupSub a (SubMap m) flattenNames xs = Just . concatMap snd $ xs n = toName a ------------------------------------------------------------- --- Primitives -------------------------------------------- ------------------------------------------------------------- +------------------------------------------------------------------------ +-- Subdiagram maps +------------------------------------------------------------------------ -- $prim -- Ultimately, every diagram is essentially a tree whose leaves are /primitives/, @@ -793,64 +752,16 @@ instance Transformable (Prim b v n) where instance Renderable (Prim b v n) b where render b (Prim p) = render b p ------------------------------------------------------------- --- Backends ----------------------------------------------- ------------------------------------------------------------- - --- | A 'DTree' is a raw tree representation of a 'QDiagram', with all --- the @u@-annotations removed. It is used as an intermediate type --- by diagrams-core; backends should not need to make use of it. --- Instead, backends can make use of 'RTree', which 'DTree' gets --- compiled and optimized to. -type DTree b v n a = Tree (DNode b v n a) - -data DNode b v n a = DStyle (Style v n) - | DTransform (Transformation v n) - | DAnnot a - | DDelay - -- ^ @DDelay@ marks a point where a delayed subtree - -- was expanded. Such subtrees already take all - -- non-frozen transforms above them into account, - -- so when later processing the tree, upon - -- encountering a @DDelay@ node we must drop any - -- accumulated non-frozen transformation. - | DPrim (Prim b v n) - | DEmpty - --- | An 'RTree' is a compiled and optimized representation of a --- 'QDiagram', which can be used by backends. They have the --- following invariant which backends may rely upon: --- --- * @RPrim@ nodes never have any children. -type RTree b v n a = Tree (RNode b v n a) - -data RNode b v n a = RStyle (Style v n) -- ^ A style node. - | RAnnot a - | RPrim (Prim b v n) -- ^ A primitive. - | REmpty - --- | Prism onto a style of an 'RNode'. -_RStyle :: Prism' (RNode b v n a) (Style v n) -_RStyle = prism' RStyle $ \n -> case n of RStyle s -> Just s; _ -> Nothing - --- | Prism onto an annotation of an 'RNode'. -_RAnnot :: Prism' (RNode b v n a) a -_RAnnot = prism' RAnnot $ \n -> case n of RAnnot a -> Just a; _ -> Nothing - --- | Prism onto a 'Prim' of an 'RNode'. -_RPrim :: Prism' (RNode b v n a) (Prim b v n) -_RPrim = prism' RPrim $ \n -> case n of RPrim p -> Just p; _ -> Nothing - --- | Prism onto an empty 'RNode'. -_REmpty :: Prism' (RNode b v n a) () -_REmpty = prism' (const REmpty) $ \n -> case n of REmpty -> Just (); _ -> Nothing +------------------------------------------------------------------------ +-- Backends +------------------------------------------------------------------------ -- | Abstract diagrams are rendered to particular formats by -- /backends/. Each backend/vector space combination must be an -- instance of the 'Backend' class. -- -- A minimal complete definition consists of 'Render', 'Result', --- 'Options', and 'renderRTree'. However, most backends will want to +-- 'Options', and 'renderDia'. However, most backends will want to -- implement 'adjustDia' as well; the default definition does -- nothing. Some useful standard definitions are provided in the -- @Diagrams.TwoD.Adjust@ module from the @diagrams-lib@ package. @@ -887,7 +798,7 @@ class Backend b v n where -- | Given some options, take a representation of a diagram as a -- tree and render it. The 'RTree' has already been simplified -- and has all measurements converted to @Output@ units. - renderRTree :: b -> Options b v n -> RTree b v n Annotation -> Result b v n + renderDUAL :: Monoid' m => b -> Options b v n -> Transformation v n -> QDiagram b v n m -> Result b v n -- See Note [backend token] @@ -1001,7 +912,7 @@ instance Backend NullBackend v n where type Result NullBackend v n = () data Options NullBackend v n - renderRTree _ _ _ = () + renderDUAL _ _ _ _ = () -- | The Renderable type class connects backends to primitives which -- they know how to render. diff --git a/src/Diagrams/Core/V.hs b/src/Diagrams/Core/V.hs index a3c9a0d..4b0e126 100644 --- a/src/Diagrams/Core/V.hs +++ b/src/Diagrams/Core/V.hs @@ -25,6 +25,11 @@ import Data.Monoid.Deletable import Data.Monoid.Split import Data.Semigroup import Data.Set +import Data.IntMap +import Data.Tree +import Data.HashMap.Lazy +import Data.HashSet +import Data.Sequence import Linear.Vector @@ -41,14 +46,21 @@ type family V a :: * -> * -- Note, to use these instances one often needs a constraint of the form -- V a ~ V b, etc. -type instance V (a,b) = V a -type instance V (a,b,c) = V a - -type instance V (a -> b) = V b -type instance V [a] = V a -type instance V (Option a) = V a -type instance V (Set a) = V a -type instance V (Map k a) = V a +type instance V (a,b) = V a +type instance V (a,b,c ) = V a +type instance V (a,b,c,d) = V a + +type instance V (a -> b) = V b +type instance V [a] = V a +type instance V (Option a) = V a +type instance V (Set a) = V a +type instance V (Map k a) = V a +type instance V (IntMap a) = V a +type instance V (Seq a) = V a +type instance V (Tree a) = V a +type instance V (Maybe a) = V a +type instance V (HashMap k a) = V a +type instance V (HashSet a) = V a type instance V (Deletable m) = V m type instance V (Split m) = V m @@ -57,14 +69,21 @@ type instance V (m :+: n) = V m -- | The numerical field for the object, the number type used for calculations. type family N a :: * -type instance N (a,b) = N a -type instance N (a,b,c) = N a +type instance N (a,b) = N a +type instance N (a,b,c) = N a +type instance N (a,b,c,d) = N a type instance N (a -> b) = N b type instance N [a] = N a type instance N (Option a) = N a type instance N (Set a) = N a type instance N (Map k a) = N a +type instance N (IntMap a) = N a +type instance N (Seq a) = N a +type instance N (Tree a) = N a +type instance N (Maybe a) = N a +type instance N (HashMap k a) = N a +type instance N (HashSet a) = N a type instance N (Deletable m) = N m type instance N (Split m) = N m