{-# LANGUAGE ConstrainedClassMethods #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
module Diagrams.Align
(
Alignable(..)
, alignBy'Default
, envelopeBoundary
, traceBoundary
, align
, snug
, centerV, center
, snugBy
, snugCenterV, snugCenter
) where
import Diagrams.Core
import Diagrams.Util (applyAll)
import Data.Maybe (fromMaybe)
import Data.Ord (comparing)
import Data.Traversable
import Prelude
import qualified Data.Foldable as F
import qualified Data.Map as M
import qualified Data.Set as S
import Linear.Affine
import Linear.Metric
import Linear.Vector
class Alignable a where
alignBy' :: (InSpace v n a, Fractional n, HasOrigin a)
=> (v n -> a -> Point v n) -> v n -> n -> a -> a
alignBy' = alignBy'Default
defaultBoundary :: (V a ~ v, N a ~ n) => v n -> a -> Point v n
alignBy :: (InSpace v n a, Fractional n, HasOrigin a)
=> v n -> n -> a -> a
alignBy = alignBy' defaultBoundary
alignBy'Default :: (InSpace v n a, Fractional n, HasOrigin a)
=> (v n -> a -> Point v n) -> v n -> n -> a -> a
alignBy'Default boundary v d a = moveOriginTo (lerp ((d + 1) / 2)
(boundary v a)
(boundary (negated v) a)
) a
{-# ANN alignBy'Default ("HLint: ignore Use camelCase" :: String) #-}
envelopeBoundary :: (V a ~ v, N a ~ n, Enveloped a) => v n -> a -> Point v n
envelopeBoundary = envelopeP
traceBoundary :: (V a ~ v, N a ~ n, Num n, Traced a) => v n -> a -> Point v n
traceBoundary v a = fromMaybe origin (maxTraceP origin v a)
combineBoundaries
:: (InSpace v n a, Metric v, Ord n, F.Foldable f)
=> (v n -> a -> Point v n) -> v n -> f a -> Point v n
combineBoundaries b v fa
= b v $ F.maximumBy (comparing (quadrance . (.-. origin) . b v)) fa
instance (Metric v, OrderedField n) => Alignable (Envelope v n) where
defaultBoundary = envelopeBoundary
instance (Metric v, OrderedField n) => Alignable (Trace v n) where
defaultBoundary = traceBoundary
instance (V b ~ v, N b ~ n, Metric v, OrderedField n, Alignable b) => Alignable [b] where
defaultBoundary = combineBoundaries defaultBoundary
instance (V b ~ v, N b ~ n, Metric v, OrderedField n, Alignable b)
=> Alignable (S.Set b) where
defaultBoundary = combineBoundaries defaultBoundary
instance (V b ~ v, N b ~ n, Metric v, OrderedField n, Alignable b)
=> Alignable (M.Map k b) where
defaultBoundary = combineBoundaries defaultBoundary
instance (Metric v, OrderedField n, Monoid' m)
=> Alignable (QDiagram b v n m) where
defaultBoundary = envelopeBoundary
instance (InSpace v n a, HasOrigin a, Alignable a) => Alignable (b -> a) where
alignBy v d f b = alignBy v d (f b)
defaultBoundary _ _ = origin
align :: (InSpace v n a, Fractional n, Alignable a, HasOrigin a) => v n -> a -> a
align v = alignBy v 1
snugBy :: (InSpace v n a, Fractional n, Alignable a, Traced a, HasOrigin a)
=> v n -> n -> a -> a
snugBy = alignBy' traceBoundary
snug :: (InSpace v n a, Fractional n, Alignable a, Traced a, HasOrigin a)
=> v n -> a -> a
snug v = snugBy v 1
centerV :: (InSpace v n a, Fractional n, Alignable a, HasOrigin a) => v n -> a -> a
centerV v = alignBy v 0
center :: (InSpace v n a, Fractional n, Traversable v, Alignable a, HasOrigin a) => a -> a
center = applyAll fs
where
fs = map centerV basis
snugCenterV
:: (InSpace v n a, Fractional n, Alignable a, Traced a, HasOrigin a)
=> v n -> a -> a
snugCenterV v = alignBy' traceBoundary v 0
snugCenter :: (InSpace v n a, Traversable v, Fractional n, Alignable a, HasOrigin a, Traced a)
=> a -> a
snugCenter = applyAll fs
where
fs = map snugCenterV basis
{-# ANN module ("HLint: ignore Use camelCase" :: String) #-}