plots-0.1.1.4: src/Plots/Axis/Render.hs
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE MultiWayIf #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE ViewPatterns #-}
-- Orphan Mainable Axis instance.
{-# OPTIONS_GHC -fno-warn-orphans #-}
-----------------------------------------------------------------------------
-- |
-- Module : Plots.Axis.Render
-- Copyright : (C) 2016 Christopher Chalmers
-- License : BSD-style (see the file LICENSE)
-- Maintainer : Christopher Chalmers
-- Stability : experimental
-- Portability : non-portable
--
-- Low level module containing functions for rendering different types
-- of axis.
--
----------------------------------------------------------------------------
module Plots.Axis.Render
( -- * Rendering axes
RenderAxis (..)
, r2AxisMain
-- * Low level
, buildPlots
)where
import Data.Bool
import Data.Foldable as F
import Data.List (sort)
import Data.Typeable
import Diagrams.BoundingBox
import Diagrams.Prelude
import Diagrams.TwoD.Text
import Linear hiding (rotate, translation)
import Diagrams.Backend.CmdLine
import Diagrams.Coordinates.Polar
import Plots.Axis
import Plots.Axis.ColourBar
import Plots.Axis.Grid
import Plots.Axis.Labels
import Plots.Axis.Line
import Plots.Axis.Scale
import Plots.Axis.Ticks
import Plots.Axis.Title
import Plots.Legend
import Plots.Style
import Plots.Types
import Plots.Util
import Prelude
------------------------------------------------------------------------
-- Mainable instances
------------------------------------------------------------------------
instance (TypeableFloat n,
Renderable (Path V2 n) b,
Mainable (QDiagram b V2 n Any))
=> Mainable (Axis b Polar n) where
type MainOpts (Axis b Polar n) = MainOpts (QDiagram b V2 n Any)
mainRender opts = mainRender opts . renderAxis
instance (TypeableFloat n,
Renderable (Path V2 n) b,
Mainable (QDiagram b V2 n Any))
=> Mainable (Axis b V2 n) where
type MainOpts (Axis b V2 n) = MainOpts (QDiagram b V2 n Any)
mainRender opts = mainRender opts . renderAxis
instance ToResult (Axis b v n) where
type Args (Axis b v n) = ()
type ResultOf (Axis b v n) = Axis b v n
toResult d _ = d
-- | 'mainWith' specialised to a 2D Axis.
r2AxisMain
:: (Parseable (MainOpts (QDiagram b V2 Double Any)),
Mainable (Axis b V2 Double))
=> Axis b V2 Double
-> IO ()
r2AxisMain = mainWith
------------------------------------------------------------------------
-- Low level functions
------------------------------------------------------------------------
-- | Build a list of styled plots from the axis, ready to be rendered.
-- This takes into account any 'AxisStyle' changes and applies the
-- 'finalPlots' modifications.
--
-- The 'StyledPlots' can be rendered with 'renderStyledPlot' and the
-- legend entries can be obtained with 'styledPlotLegends'. This is
-- what 'renderAxis' can uses internally but might be useful for
-- debugging or generating your own legend.
buildPlots :: BaseSpace c ~ v => Axis b c n -> [StyledPlot b v n]
buildPlots a = map (appEndo $ a ^. plotModifier)
$ zipWith styleDynamic (a ^.. axisStyles) (a ^. axisPlots)
-- TODO: correct order
------------------------------------------------------------------------
-- Render axis
------------------------------------------------------------------------
-- | Renderable axes.
class RenderAxis b v n where
-- | Render an axis to a diagram. The size of the diagram is
-- determined by the 'axisSize'.
renderAxis :: Axis b v n -> QDiagram b (BaseSpace v) n Any
-- | The 'RenderAxis' class provides a default way to render an axis for
-- each space.
instance (TypeableFloat n, Renderable (Path V2 n) b)
=> RenderAxis b V2 n where
-- | Render an axis and its plots, as well as the legend and colour
-- bar.
renderAxis = renderR2Axis
renderR2Axis :: (TypeableFloat n, Renderable (Path V2 n) b)
=> Axis b V2 n -> QDiagram b V2 n Any
renderR2Axis a = frame 40
$ leg
<> ttl
<> cBar
<> plots
<> drawAxis ex ey LowerLabels
<> drawAxis ey ex LeftLabels
where
spec = AxisSpec xs t (a^.axes . column logScale) (a ^. axisColourMap)
plots = foldMap (renderStyledPlot spec) styledPlots
drawAxis ll ll2 = axisOnBasis origin xs (a^.axes.el ll) (a^.axes.column logScale) t ll ll2
--
(xs, tv, t') = calculateScaling (a^.axes.column axisScaling) (boundingBox styledPlots)
t = tv <> t'
--
bb = fromCorners (P . apply t $ fmap fst xs) (P . apply t $ fmap snd xs)
leg = drawLegend bb (styledPlotLegends styledPlots) (a ^. legend)
--
-- The colour bar
cBar = addColourBar bb (a^.colourBar) (a ^. axisColourMap) (a^.colourBarRange)
-- title
ttl = drawTitle bb (a^.title)
--
styledPlots = buildPlots a
-- | The position of axis labels for a
data LabelPosition
= NoLabels
| LowerLabels
| LeftLabels
| RightLabels
| UpperLabels
deriving (Show, Eq, Typeable)
axisOnBasis
:: forall b v n. (v ~ V2, TypeableFloat n, HasLinearMap v, Metric v,
Renderable (Path V2 n) b, n ~ N (v n), v ~ V (v n), OrderedField n)
=> Point v n -- start of axis
-> v (n, n) -- calculated bounds
-> SingleAxis b v n -- axis data
-> v LogScale -- log scale
-> T2 n -- transformation to apply to positions of things
-> E v -- direction of axis
-> E v -- orthogonal direction of axis
-> LabelPosition -- where (if at all) should labels be placed?
-> QDiagram b V2 n Any -- resulting axis
axisOnBasis p bs a ls t e eO lp
| a ^. hidden = phantom axis
| otherwise = axis
where
axis = tickLabels <> axLabels <> ticks <> line <> grid
tStroke = stroke . transform t
-- axis labels (x,y etc.)
axLabels
| null txt || lp == NoLabels || a ^. axisLabel . hidden
= mempty
| otherwise = (a ^. axisLabelTextFunction) txtAlign txt
# moveTo p'
# applyStyle (a ^. axisLabelStyle)
where
p' = p & ep e .~ x
& ep eO .~ y0
-- & logPoint ls
& coscale
& papply t
& ep eO +~ negate' labelGap
labelGap = a ^. axisLabelGap
txt = a ^. axisLabelText
x = case a ^. axisLabelPosition of
MiddleAxisLabel -> (x0 + x1) / 2
LowerAxisLabel -> x0
UpperAxisLabel -> x1
-- axLabelD = a ^. axisLabels . el e
-- tick labels
tickLabels
| lp == NoLabels || a ^. tickLabel . hidden = mempty
| otherwise = foldMap drawLabels (map snd $ take 1 ys)
# applyStyle (a ^. tickLabelStyle)
where
-- tickLabelsD = a ^. axisTickLabels . el e
labelFun = a ^. tickLabelFunction
drawLabels y = foldMap f (labelFun (filter inRange majorTickXs) b)
where
f (x, l) = place dia p'
where
dia = view tickLabelTextFunction a txtAlign l
p' = p & ep e .~ x
& ep eO .~ y
-- & logPoint ls
& coscale
& papply t
& ep eO +~ negate' (a ^. tickLabelGap)
-- the grid
grid = majorLines <> minorLines
where
majorLines
| a ^. majorGridLines . hidden = mempty
| otherwise = foldMap mkGridLine majorGridXs'
# tStroke
# applyStyle (a ^. majorGridLinesStyle)
majorGridXs = view majorGridLinesFunction a majorTickXs b
majorGridXs' = map coscaleNum (filter inRange majorGridXs)
--
minorLines
| a ^. minorGridLines . hidden = mempty
| otherwise = foldMap mkGridLine minorGridXs'
# tStroke
# applyStyle (a ^. minorGridLinesStyle)
minorGridXs = view minorGridLinesFunction a minorTickXs b
minorGridXs' = map coscaleNum (filter inRange minorGridXs)
-- --
mkGridLine x = pathFromVertices [f y0, f y1]
where f y = over lensP ((el e .~ x) . (el eO .~ y)) p
--
-- gridD = a ^. axisGridLines ^. el e -- :: GridLines N
-- the ticks
ticks = foldMap drawTicks ys
drawTicks (pos,y) = maTicks <> miTicks
where
maTicks
| a ^. majorTicks . hidden = mempty
| otherwise = foldMap (positionTick majorTick) majorTickXs'
# stroke
# applyStyle (a ^. majorTicksStyle)
--
miTicks
| a ^. minorTicks . hidden = mempty
| otherwise = foldMap (positionTick minorTick) minorTickXs'
# stroke
# applyStyle (a ^. minorTicksStyle)
--
minorTick = someTick (a ^. minorTicksAlignment) (a ^. minorTicksLength)
majorTick = someTick (a ^. majorTicksAlignment) (a ^. majorTicksLength)
--
someTick tType d = pathFromVertices $
case tType of
AutoTick ->
case pos of
LowerAxis -> [origin & ep eO -~ d, origin]
MiddleAxis -> [origin & ep eO -~ d, origin & ep eO +~ d]
UpperAxis -> [origin, origin & ep eO +~ d]
TickSpec (fromRational -> aa) (fromRational -> bb) ->
case pos of
UpperAxis -> [origin & ep eO -~ d*bb, origin & ep eO +~ d*aa]
_ -> [origin & ep eO -~ d*aa, origin & ep eO +~ d*bb]
-- NoTick -> []
-- middleTick d =
-- pathFromVertices
positionTick tick x = place tick p'
where
p' = over lensP ((el e .~ x) . (el eO .~ y)) p
# transform t
-- axis lines
line
| a ^. axisLine . hidden = mempty
| otherwise = foldMap mkline (map snd ys) -- merge with ticks?
# transform t
# stroke
# lineCap LineCapSquare
# applyStyle (a^.axisLineStyle)
where
-- TODO: Arrow for R3
mkline y = pathFromVertices
$ map (\x -> over lensP ((el e .~ x) . (el eO .~ y)) p) [x0, x1] :: Path v n
-- measurements
b@(x0,x1) = bs ^. el e :: (n, n) -- bounds
coscale = ep e %~ coscaleNum
coscaleNum = scaleNum (bs ^. el e) (ls ^. el e)
yb@(y0,y1) = bs ^. el eO . if lp == UpperLabels
then swapped
else id
inRange x = x >= x0 && x <= x1
--
majorTickXs = sort $ view majorTicksFunction a b
majorTickXs' = map coscaleNum (filter inRange majorTickXs)
minorTickXs = sort $ view minorTicksFunction a majorTickXs b
minorTickXs' = map coscaleNum (filter inRange minorTickXs)
--
ys = getAxisLinePos yb lineType
lineType = a ^. axisLineType
txtAlign =
case lp of
LowerLabels -> BoxAlignedText 0.5 1
LeftLabels -> BoxAlignedText 1 0.5
RightLabels -> BoxAlignedText 0 0.5
UpperLabels -> BoxAlignedText 1 0
_ -> error "No labels" -- XXX Temporary
-- t2 = scaling 4
--
negate' = if lp == UpperLabels || lp == RightLabels
then id
else negate
-- utilities
getAxisLinePos :: (Num n, Ord n) => (n, n) -> AxisLineType -> [(AxisPos, n)]
getAxisLinePos (a,b) aType = case aType of
BoxAxisLine -> [(LowerAxis, a), (UpperAxis, b)]
LeftAxisLine -> [(LowerAxis, a)]
MiddleAxisLine -> [(,) MiddleAxis $
if | a > 0 -> a
| b < 0 -> b
| otherwise -> 0]
RightAxisLine -> [(UpperAxis, b)]
NoAxisLine -> []
data AxisPos = LowerAxis | MiddleAxis | UpperAxis
------------------------------------------------------------------------
-- Utilities
------------------------------------------------------------------------
ep :: E v -> Lens' (Point v x) x
ep (E l) = lensP . l
{-# INLINE ep #-}
------------------------------------------------------------------------
-- Polar
------------------------------------------------------------------------
instance (TypeableFloat n, Renderable (Path V2 n) b)
=> RenderAxis b Polar n where
renderAxis = renderPolarAxis
-- | An lower and upper bound for the bounding radius using @n@ envelope
-- calculations. The more calculations used, the smaller the range of
-- the bound.
boundingRadiusR :: (InSpace V2 n a, Enveloped a) => Int -> a -> (n, n)
boundingRadiusR (max 3 -> n) e =
case appEnvelope (getEnvelope e) of
Nothing -> (0,0)
Just f ->
let thetas = map (@@rad) $ enumFromToN 0 tau n
vs = map angleV thetas
-- The lower bound is the maximum distance obtained from the
-- envelope trials. We know the radius will be at least this far.
lowerBound = F.foldr (\v r -> max (f v) r) 0 vs
-- In the worst case, there will be a point at the intersection of
-- two neighbouring bounding planes from the envelope calculations.
-- We can calculate the distance to this intersecion using simple
-- trigonometry.
-- (Note, this is why we need at least three envelope calculations,
-- otherwise there wouldn't be any intersection between the bounding
-- planes)
upperBound = lowerBound / cos (pi / fromIntegral n)
in (lowerBound, upperBound)
renderPolarAxis
:: (TypeableFloat n, Renderable (Path V2 n) b)
=> Axis b Polar n -> QDiagram b V2 n Any
renderPolarAxis a = frame 15
$ leg
-- <> colourBar
-- <> circles
<> plots
<> theAxis
where
r = snd $ boundingRadiusR 30 styledPlots
spec = AxisSpec xs t (pure LinearAxis) (a ^. axisColourMap)
plots = F.foldMap (renderStyledPlot spec) styledPlots
dataBB = fromCorners (mkP2 (-r) (-r)) (mkP2 r r)
(xs, tv, t') = calculateScaling (view _Wrapped $ a^.axes.column axisScaling) dataBB
t = tv <> t'
--
theAxis = drawPolarAxis spec (a ^. axes)
--
bb = fromCorners (P . apply t $ fmap fst xs) (P . apply t $ fmap snd xs)
leg = drawLegend bb (styledPlotLegends styledPlots) (a ^. legend)
--
styledPlots = map (appEndo $ a ^. plotModifier)
$ zipWith styleDynamic (a ^.. axisStyles) (a ^. axisPlots)
drawPolarAxis
:: forall b n. (Renderable (Path V2 n) b, TypeableFloat n)
=> AxisSpec V2 n -> Polar (SingleAxis b V2 n) -> QDiagram b V2 n Any
drawPolarAxis spec (Polar (V2 rA thetaA)) = fcA transparent $ rAx <> thetaAx where
-- use a radius of the upper x bound for the axis (this is not ideal)
r = spec ^. specBounds . _x . _2
t = spec ^. specTrans
s = avgScale t
rInRange x = x >= 0 && x <= r*1.000001
thetaInRange x = x >= 0 && x < tau
----------------------------------------------------------------------
-- Radial axis
----------------------------------------------------------------------
-- The radial axis consists of an axis line from the centre to the
-- edge. The ticks and tickLabels are along this line. The grid lines
-- are made up of circles that pass through the line, centered at the
-- center of the plot.
rAx
| rA ^. hidden = mempty
| otherwise = rAxLine <> rAxLabel <> rAxTicks <> rAxTickLabels <> rAxGridLines
rAxLine = line # whenever (rA ^. axisLine . hidden) phantom
where
-- XXX for now the radial axis is on the theta=0 line. Need some
-- way to change this
line = (origin ~~ mkP2 r 0) # applyStyle (rA^.axisLineStyle)
# transform t
-- Radial axis label -------------------------------------------------
rAxLabel
| null rTxt || rA ^. axisLabel . hidden = mempty
| otherwise = view axisLabelTextFunction rA rLabelAlign rTxt
# translate rLabelPos
# applyStyle (rA ^. axisLabelStyle)
# fc black
rLabelPos = V2 (s*x) (- view axisLabelGap rA) where
x = case rA ^. axisLabelPosition of
MiddleAxisLabel -> r/2
LowerAxisLabel -> 0
UpperAxisLabel -> r
rTxt = rA ^. axisLabelText
rLabelAlign = BaselineText
-- Radial ticks ------------------------------------------------------
-- The positions of major and minor ticks along the radial axis
majorTickRs = view majorTicksFunction rA (0,r)
majorTickRs' = map (*s) $ filter rInRange majorTickRs
minorTickRs = view minorTicksFunction rA majorTickRs (0,r)
minorTickRs' = map (*s) $ filter rInRange minorTickRs
-- Major and minor ticks are placed along the line at the calculated
-- positions majorTickRs
rAxTicks = rAxMajorTicks <> rAxMinorTicks
rAxMajorTicks
| rA ^. majorTicks . hidden = mempty
| otherwise = F.foldMap (\x -> rAxMajorTick # translateX x) majorTickRs'
# applyStyle (rA ^. majorTicksStyle)
rAxMinorTicks
| rA ^. minorTicks . hidden = mempty
| otherwise = F.foldMap (\x -> rAxMinorTick # translateX x) minorTickRs'
# applyStyle (rA ^. minorTicksStyle)
-- The paths used for individual major and minor ticks
rAxMajorTick = someTick (rA ^. majorTicksAlignment) (rA ^. majorTicksLength)
rAxMinorTick = someTick (rA ^. minorTicksAlignment) (rA ^. minorTicksLength)
someTick tType d = case tType of
TickSpec (fromRational -> aa) (fromRational -> bb)
-> mkP2 0 (-d*bb) ~~ mkP2 0 (d*aa)
AutoTick -> mkP2 0 (-d) ~~ mkP2 0 d
-- Radial grid lines -------------------------------------------------
rAxGridLines
-- - | rA ^. gridLines . hidden = mempty
| otherwise = rMajorGridLines <> rMinorGridLines
majorGridRs = view majorGridLinesFunction rA majorTickRs (0,r)
majorGridRs' = map (*s) $ filter rInRange majorGridRs
rMajorGridLines :: QDiagram b V2 n Any
rMajorGridLines
| rA ^. majorGridLines . hidden = mempty
| otherwise = F.foldMap circle (filter (>0) majorGridRs')
# applyStyle (rA ^. majorGridLinesStyle)
minorGridRs = view minorGridLinesFunction rA minorTickRs (0,r)
minorGridRs' = map (*s) $ filter rInRange minorGridRs
rMinorGridLines :: QDiagram b V2 n Any
rMinorGridLines
| rA ^. minorGridLines . hidden = mempty
| otherwise = F.foldMap circle (filter (>0) minorGridRs')
# applyStyle (rA ^. minorGridLinesStyle)
-- Radial tick labels ------------------------------------------------
rAxTickLabels :: QDiagram b V2 n Any
rAxTickLabels
| rA ^. tickLabel . hidden = mempty
| otherwise = F.foldMap rDrawTickLabel tickLabelRs
-- The positions of the tick labels.
tickLabelRs :: [(n, String)]
tickLabelRs = view tickLabelFunction rA (filter rInRange majorTickRs) (0,r)
-- Draw a single tick label given the position and the string to use
rDrawTickLabel :: (n,String) -> QDiagram b V2 n Any
rDrawTickLabel (x,label) =
view tickLabelTextFunction rA (BoxAlignedText 0.5 1) label
# translate (V2 (s*x) (- view axisLabelGap rA))
# applyStyle (rA ^. tickLabelStyle)
# fc black
----------------------------------------------------------------------
-- Angular axis
----------------------------------------------------------------------
-- The angular axis is a circular line around the perimeter of the
-- polar plot. The Ticks and tick labels are placed around this
-- perimeter. The Grid lines go from the perimeter to the center.
thetaAx
| thetaA ^. hidden = mempty
| otherwise = thetaAxLine <> thetaAxLabel
<> thetaAxTicks <> thetaAxTickLabels <> thetaAxGridLines
theta = 2*pi
thetaAxLine = line # whenever (thetaA ^. axisLine . hidden) phantom
where
-- XXX for now the radial axis is on the theta=0 line. Need some
-- way to change this
line = circle (s*r) # applyStyle (thetaA^.axisLineStyle)
-- Angular axis label ------------------------------------------------
-- Where should the label go?
thetaAxLabel
| null thetaTxt || thetaA ^. axisLabel . hidden = mempty
| otherwise = view axisLabelTextFunction thetaA thetaLabelAlign thetaTxt
# translate thetaLabelPos
# applyStyle (thetaA ^. axisLabelStyle)
# fc black
-- thetaLabelPos = V2 (s*x) (- view axisLabelGap thetaA) where
thetaLabelPos = view xy_ (mkPolar (s*r + view axisLabelGap thetaA) x) where
-- The angle on the axis the label is placed, doesn't make much
-- sense right now.
x = case thetaA ^. axisLabelPosition of
MiddleAxisLabel -> quarterTurn
LowerAxisLabel -> zero
UpperAxisLabel -> halfTurn
thetaTxt = thetaA ^. axisLabelText
thetaLabelAlign = BaselineText
-- Angular axis ticks ------------------------------------------------
-- The positions of major and minor ticks along the angular axis
majorTickThetas = view majorTicksFunction thetaA (0,theta)
majorTickThetas' = filter thetaInRange majorTickThetas
minorTickThetas = view minorTicksFunction thetaA majorTickThetas (0,theta)
minorTickThetas' = filter thetaInRange minorTickThetas
-- Major and minor ticks are placed along perimeter, facing the center
-- of the axis. Ticks start of horizonal and are rotated to the
-- correct position on the axis.
thetaAxTicks = thetaAxMajorTicks <> thetaAxMinorTicks
thetaAxMajorTicks
| thetaA ^. majorTicks . hidden = mempty
| otherwise = F.foldMap (\phi -> thetaAxMajorTick # translateX (s*r) # rotate (phi@@rad)) majorTickThetas'
# applyStyle (thetaA ^. majorTicksStyle)
thetaAxMinorTicks
| thetaA ^. minorTicks . hidden = mempty
| otherwise = F.foldMap (\phi -> thetaAxMinorTick # translateX (s*r) # rotate (phi@@rad)) minorTickThetas'
# applyStyle (thetaA ^. minorTicksStyle)
-- The paths used for individual major and minor ticks
thetaAxMajorTick = someThetaTick (thetaA ^. majorTicksAlignment) (thetaA ^. majorTicksLength)
thetaAxMinorTick = someThetaTick (thetaA ^. minorTicksAlignment) (thetaA ^. minorTicksLength)
someThetaTick tType d = case tType of
TickSpec (fromRational -> aa) (fromRational -> bb)
-> mkP2 (-d*bb) 0 ~~ mkP2 (d*aa) 0
AutoTick -> mkP2 (-d) 0 ~~ mkP2 d 0
-- Angular grid lines ------------------------------------------------
-- grid lines go from the centre of the axis to the perimeter
thetaAxGridLines
-- - | thetaA ^. gridLines . hidden = mempty
| otherwise = thetaMajorGridLines <> thetaMinorGridLines
majorGridThetas = view majorGridLinesFunction thetaA majorTickThetas (0,theta)
majorGridThetas' = filter thetaInRange majorGridThetas
thetaMajorGridLines :: QDiagram b V2 n Any
thetaMajorGridLines
| thetaA ^. majorGridLines . hidden = mempty
| otherwise = F.foldMap (\phi -> origin ~~ mkP2 r 0 # rotate (phi@@rad)) majorGridThetas'
# transform t
# applyStyle (thetaA ^. majorGridLinesStyle)
minorGridThetas = view minorGridLinesFunction thetaA minorTickThetas (0,theta)
minorGridThetas' = filter thetaInRange minorGridThetas
thetaMinorGridLines :: QDiagram b V2 n Any
thetaMinorGridLines
| thetaA ^. minorGridLines . hidden = mempty
| otherwise = F.foldMap (\phi -> origin ~~ mkP2 r 0 # rotate (phi@@rad)) minorGridThetas'
# transform t
# applyStyle (thetaA ^. minorGridLinesStyle)
-- Angular tick labels -----------------------------------------------
thetaAxTickLabels :: QDiagram b V2 n Any
thetaAxTickLabels
| thetaA ^. tickLabel . hidden = mempty
| otherwise = F.foldMap thetaDrawTickLabel tickLabelThetas
-- The positions of the tick labels.
tickLabelThetas :: [(n, String)]
tickLabelThetas = view tickLabelFunction thetaA majorTickThetas' (0,theta)
-- Draw a single tick label given the position and the string to use
thetaDrawTickLabel :: (n, String) -> QDiagram b V2 n Any
thetaDrawTickLabel (x,label) =
view tickLabelTextFunction thetaA a label
# translate v
# applyStyle (thetaA ^. tickLabelStyle)
# fc black
where v = mkPolar (s*r + view axisLabelGap thetaA) (x@@rad) ^. xy_
-- a = BoxAlignedText (0.5-cos x/2) (0.5-sin x/2)
a = BoxAlignedText 0.5 0.5
-- line
-- | a ^. axisLine . hidden = mempty
-- | otherwise = F.foldMap mkline (map snd ys) -- merge with ticks?
-- # transform t
-- # stroke
-- -- # applyStyle (a ^. axisLine e . axisArrowOpts . _Just . shaftStyle)
-- # lineCap LineCapSquare
-- where
-- -- TODO: Arrow for R3
-- mkline y = pathFromVertices
-- $ map (\x -> over lensP ((el e .~ x) . (el eO .~ y)) p) [x0, x1] :: Path v n