{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies #-}
module Plots.Axis.Ticks
(
MajorTicks
, HasMajorTicks (..)
, majorTicksHelper
, logMajorTicks
, MinorTicks
, HasMinorTicks (..)
, minorTicksHelper
, Ticks
, HasTicks (..)
, ticksAlign
, ticksStyle
, ticksVisible
, TicksAlignment (..)
, autoTicks
, centreTicks
, centerTicks
, insideTicks
, outsideTicks
, hideTicks
, majorTickPositions
, minorTickPositions
, linearMajorTicks
) where
import Control.Lens hiding (transform, ( # ))
import Data.Default
import Data.Foldable as F
import Data.Ord
import Plots.Types
import Plots.Util
import Diagrams.Prelude
data TicksAlignment
= TickSpec !Rational !Rational
| AutoTick
deriving (Show, Eq)
autoTicks :: TicksAlignment
autoTicks = AutoTick
centreTicks :: TicksAlignment
centreTicks = TickSpec 0.5 0.5
centerTicks :: TicksAlignment
centerTicks = centreTicks
insideTicks :: TicksAlignment
insideTicks = TickSpec 0 1
outsideTicks :: TicksAlignment
outsideTicks = TickSpec 1 0
data MajorTicks v = MajorTicks
{ matFunction :: (Double,Double) -> [Double]
, matAlign :: TicksAlignment
, matLength :: Double
, matStyle :: Style v Double
, matVisible :: Bool
}
instance Default (MajorTicks v) where
def = MajorTicks
{ matFunction = linearMajorTicks 5
, matAlign = autoTicks
, matLength = 5
, matStyle = mempty # lwO 0.4
, matVisible = True
}
type instance V (MajorTicks v) = v
type instance N (MajorTicks v) = Double
class HasMajorTicks f a where
majorTicks :: LensLike' f a (MajorTicks (V a))
majorTicksFunction :: Functor f => LensLike' f a ((Double, Double) -> [Double])
majorTicksFunction = majorTicks . lens matFunction (\mat a -> mat {matFunction = a})
majorTicksAlignment :: Functor f => LensLike' f a TicksAlignment
majorTicksAlignment = majorTicks . lens matAlign (\mat a -> mat {matAlign = a})
majorTicksLength :: Functor f => LensLike' f a Double
majorTicksLength = majorTicks . lens matLength (\mat a -> mat {matLength = a})
majorTicksStyle :: Functor f => LensLike' f a (Style (V a) Double)
majorTicksStyle = majorTicks . lens matStyle (\mat sty -> mat {matStyle = sty})
instance HasMajorTicks f (MajorTicks v) where
majorTicks = id
instance HasVisibility (MajorTicks v) where
visible = lens matVisible (\mat b -> mat {matVisible = b})
instance ApplyStyle (MajorTicks v) where
instance HasStyle (MajorTicks v) where
style = majorTicksStyle
data MinorTicks v = MinorTicks
{ mitFunction :: [Double] -> (Double,Double) -> [Double]
, mitAlign :: TicksAlignment
, mitLength :: Double
, mitStyle :: Style v Double
, mitVisible :: Bool
}
type instance V (MinorTicks v) = v
type instance N (MinorTicks v) = Double
instance Default (MinorTicks v) where
def = MinorTicks
{ mitFunction = minorTicksHelper 4
, mitAlign = autoTicks
, mitLength = 3
, mitStyle = mempty # lwO 0.4
, mitVisible = True
}
class HasMinorTicks f a where
minorTicks :: LensLike' f a (MinorTicks (V a))
minorTicksFunction :: Functor f => LensLike' f a ([Double] -> (Double, Double) -> [Double])
minorTicksFunction = minorTicks . lens mitFunction (\mit a -> mit {mitFunction = a})
minorTicksAlignment :: Functor f => LensLike' f a TicksAlignment
minorTicksAlignment = minorTicks . lens mitAlign (\mit a -> mit {mitAlign = a})
minorTicksLength :: Functor f => LensLike' f a Double
minorTicksLength = minorTicks . lens mitLength (\mit a -> mit {mitLength = a})
minorTicksStyle :: Functor f => LensLike' f a (Style (V a) Double)
minorTicksStyle = minorTicks . lens mitStyle (\mit sty -> mit {mitStyle = sty})
instance HasMinorTicks f (MinorTicks v) where
minorTicks = id
instance HasVisibility (MinorTicks v) where
visible = lens mitVisible (\mit sty -> mit {mitVisible = sty})
instance ApplyStyle (MinorTicks v) where
instance HasStyle (MinorTicks v) where
style = minorTicksStyle
data Ticks v = Ticks (MajorTicks v) (MinorTicks v)
type instance V (Ticks v) = v
type instance N (Ticks v) = Double
class (HasMinorTicks f a, HasMajorTicks f a) => HasTicks f a where
bothTicks :: LensLike' f a (Ticks (V a))
instance Functor f => HasTicks f (Ticks v) where
bothTicks = id
instance Functor f => HasMajorTicks f (Ticks v) where
majorTicks f (Ticks ma mi) = f ma <&> \ma' -> Ticks ma' mi
instance Functor f => HasMinorTicks f (Ticks v) where
minorTicks f (Ticks ma mi) = f mi <&> \mi' -> Ticks ma mi'
instance Default (Ticks v) where
def = Ticks def def
instance ApplyStyle (Ticks v) where
applyStyle = over ticksStyle . applyStyle
ticksAlign :: (HasTicks f a, Applicative f) => LensLike' f a TicksAlignment
ticksAlign = bothTicks . aligns
where
aligns f a = (\m mn -> a & majorTicksAlignment .~ m & minorTicksAlignment .~ mn)
<$> f (a ^. majorTicksAlignment) <*> f (a ^. minorTicksAlignment)
ticksStyle :: (HasTicks f a, Applicative f) => LensLike' f a (Style (V a) Double)
ticksStyle = bothTicks . styles
where
styles f a = (\m mn -> a & majorTicksStyle .~ m & minorTicksStyle .~ mn)
<$> f (a ^. majorTicksStyle) <*> f (a ^. minorTicksStyle)
ticksVisible :: (HasTicks f a, Applicative f) => LensLike' f a Bool
ticksVisible = bothTicks . visibles
where
visibles f a = (\m mn -> a & majorTicks . visible .~ m & minorTicks. visible .~ mn)
<$> f (a ^. majorTicks . visible) <*> f (a ^. minorTicks . visible)
hideTicks :: HasTicks Identity a => a -> a
hideTicks = ticksVisible .~ False
majorTickPositions
:: (HasMajorTicks f a, Settable f)
=> LensLike' f a [Double]
majorTickPositions = majorTicksFunction . mapped
minorTickPositions
:: (HasMinorTicks f a, Settable f)
=> LensLike' f a [Double]
minorTickPositions = minorTicksFunction . mapped . mapped
linearMajorTicks :: (RealFrac n, Floating n) => n -> (n, n) -> [n]
linearMajorTicks = majorTicksHelper [1, 0.5, 0.25, 0.2, 0.3]
logMajorTicks :: (RealFrac n, Floating n) => n -> (n, n) -> [n]
logMajorTicks n (a,b) =
map (10**) $ majorTicksHelper ts n (log10 (max 2 a), log10 b)
where ts = [1,2,3,4,5,6,7,8,9]
minorTicksHelper
:: Fractional n
=> Int
-> [n]
-> (n, n)
-> [n]
minorTicksHelper n ts _ = F.concat $ go ts where
go (x1:x2:xs) = (init . tail) (enumFromToN x1 x2 (n+2)) : go (x2:xs)
go _ = []
majorTicksHelper
:: (RealFrac n, Floating n)
=> [n]
-> n
-> (n, n)
-> [n]
majorTicksHelper ts0 n (a,b) = iterateN n' (+h) a'
where
i = fromIntegral (floor ( a / h ) :: Int)
a' = i*h
n' = ceiling ((b - a')/h) + 1
h = minimumBy (comparing $ abs . (h' -)) ts'
h' = d / n
ts' = map (* 10 ^^ (floor $ log10 d :: Int)) (ts0 ++ map (*10) ts0)
d = abs $ b - a
log10 :: Floating a => a -> a
log10 = logBase 10