{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE ViewPatterns #-}
module Diagrams.Size
(
SizeSpec
, mkSizeSpec
, dims
, absolute
, getSpec
, specToSize
, requiredScale
, requiredScaling
, sized
, sizedAs
, sizeAdjustment
) where
import Control.Applicative
import Control.Lens hiding (transform)
import Control.Monad
import Data.Foldable as F
import Data.Hashable
import Data.Maybe
import Data.Semigroup
import Data.Typeable
import GHC.Generics (Generic)
import Prelude
import Diagrams.BoundingBox
import Diagrams.Core
import Linear.Affine
import Linear.Vector
newtype SizeSpec v n = SizeSpec (v n)
deriving (
Typeable,
Functor,
Generic,
Hashable,
Show)
type instance V (SizeSpec v n) = v
type instance N (SizeSpec v n) = n
getSpec :: (Functor v, Num n, Ord n) => SizeSpec v n -> v (Maybe n)
getSpec (SizeSpec sp) = mfilter (>0) . Just <$> sp
mkSizeSpec :: (Functor v, Num n) => v (Maybe n) -> SizeSpec v n
mkSizeSpec = dims . fmap (fromMaybe 0)
dims :: v n -> SizeSpec v n
dims = SizeSpec
absolute :: (Additive v, Num n) => SizeSpec v n
absolute = SizeSpec zero
specToSize :: (Foldable v, Functor v, Num n, Ord n) => n -> SizeSpec v n -> v n
specToSize n (getSpec -> spec) = fmap (fromMaybe smallest) spec
where
smallest = fromMaybe n $ minimumOf (folded . _Just) spec
requiredScale :: (Additive v, Foldable v, Fractional n, Ord n)
=> SizeSpec v n -> v n -> n
requiredScale (getSpec -> spec) sz
| allOf (folded . _Just) (<= 0) usedSz = 1
| otherwise = fromMaybe 1 mScale
where
usedSz = liftI2 (<$) sz spec
scales = liftI2 (^/) spec sz
mScale = minimumOf (folded . _Just) scales
requiredScaling :: (Additive v, Foldable v, Fractional n, Ord n)
=> SizeSpec v n -> v n -> Transformation v n
requiredScaling spec = scaling . requiredScale spec
sized :: (InSpace v n a, HasLinearMap v, Transformable a, Enveloped a)
=> SizeSpec v n -> a -> a
sized spec a = transform (requiredScaling spec (size a)) a
sizedAs :: (InSpace v n a, SameSpace a b, HasLinearMap v, Transformable a,
Enveloped a, Enveloped b)
=> b -> a -> a
sizedAs other = sized (dims $ size other)
sizeAdjustment :: (Additive v, Foldable v, OrderedField n)
=> SizeSpec v n -> BoundingBox v n -> (v n, Transformation v n)
sizeAdjustment spec bb = (sz', t)
where
v = (0.5 *^ P sz') .-. (s *^ fromMaybe origin (boxCenter bb))
sz = boxExtents bb
sz' = if allOf folded isJust (getSpec spec)
then specToSize 0 spec
else s *^ sz
s = requiredScale spec sz
t = translation v <> scaling s