{-# LANGUAGE FlexibleContexts      #-}
# LANGUAGE MultiParamTypeClasses #
{-# LANGUAGE ScopedTypeVariables   #-}
{-# LANGUAGE TemplateHaskell       #-}
# LANGUAGE TypeFamilies           #

-----------------------------------------------------------------------------
-- |
-- Module      :  Diagrams.TwoD.Sunburst
  Copyright    :   ( c ) 2013 - 14  
-- License     :  BSD-style (see LICENSE)
-- Maintainer  :  
--
-- Generation of Sunburst Partitions. A radial view of a Treemap.
--
-- The partitions are created without examining the contents of the tree nodes
-- which allows us to create a sunburst for any @Tree a@. As a consequence we cannot
-- base the size or color of the sections on the data in the tree, but only
-- on depth and number of children. Of course the code could easily be adapted
-- to handle more specific tree data.
--
  See   ,   , \"An evaluation of space - filling
  information visualizations for depicting hierarchical structures\ " , 2000 .
-- </~john.stasko/papers/ijhcs00.pdf>.
--
-----------------------------------------------------------------------------

module Diagrams.TwoD.Sunburst
  ( --  * Sunburst
    sunburst'
  , sunburst
  , SunburstOpts(..)
  , radius
  , sectionWidth
  , colors
  ) where

import           Data.Default.Class
import qualified Data.Foldable as F
import           Data.Tree
import           Diagrams.Prelude   hiding (radius)

data SunburstOpts n = SunburstOpts
  ^ Relative size of the root circle , usually 1 .
  , _sectionWidth :: n -- ^ Relative width of the sections.
  ^ Color list one for each ring .

instance Fractional n => Default (SunburstOpts n) where
  def = SunburstOpts
    { _radius       = 1.0
    , _sectionWidth = 0.3
    , _colors       = [ lightcoral, lightseagreen, paleturquoise
                      , lightsteelblue, plum, violet, coral, honeydew]}

makeLenses ''SunburstOpts

-- Section data: Will be stored in nodes of a new rose tree and used to
-- make each section of the sunburst partition.
data SData n = SData
  n-- section radius
  n-- section width
  (Direction V2 n)  -- start direction
  (Angle n)           -- sweep angle
  Int             -- number of sections
  (Colour Double) -- color

-- Make n sections (annular wedges) starting in direction d and sweeping a
sections :: (Renderable (Path V2 n) b, TypeableFloat n) =>
            SData n -> QDiagram b V2 n Any
sections (SData r s d a n c) = mconcat $ iterateN n (rotate theta) w
  where
    theta = a ^/ fromIntegral n
    w = annularWedge (s + r) r d theta # lc white # lwG 0.008 # fc c

-- Convert an arbitrary @Tree a@ to a @Tree SData@ storing the sections info
  in the nodes . If color list is shorter than depth of tree than the first
-- color of the list is repeated. If the color list is empty, lightgray is used.
toTree :: Floating n =>
          SunburstOpts n -> Tree a -> Direction V2 n -> Angle n  -> Tree (SData n)
toTree (SunburstOpts r s []) x q1 q2 =
  toTree (SunburstOpts r s (repeat lightgray)) x q1 q2
toTree (SunburstOpts r s (c:cs)) (Node _ ts) d a = Node (SData r s d a n c) ts'
  where
    n = length ts
    dt =  a ^/ fromIntegral n
    qs = [rotate (fromIntegral i *^ dt ) d  | i <- [0..n]]
    fs = toTree (SunburstOpts(r + s) s (cs ++ [c]))
    ts' = zipWith3 fs ts (take (n-1) qs) (repeat dt)

-- | Take any @Tree a@ and @SunburstOpts@ and make a sunburst partition.
--   Basically a treemap with a radial layout.
--   The root is the center of the sunburst and its circumference is divided
--   evenly according to the number of child nodes it has. Then each of those
--   sections is treated the same way.
sunburst' :: (Renderable (Path V2 n) b, TypeableFloat n) =>
             SunburstOpts n -> Tree a -> QDiagram b V2 n Any
sunburst' opts t = sunB $ toTree opts t xDir fullTurn
  where sunB (Node sd ts') = sections sd <> F.foldMap sunB ts'

-- | @sunburst@ with default opts
--
--   > import Diagrams.TwoD.Sunburst
--   > import Data.Tree (unfoldTree)
    > aTree = unfoldTree ( \n - > ( 0 , replicate n ( n-1 ) ) ) 6
    > sunburstEx = sunburst aTree # pad 1.1
--
--   <<diagrams/src_Diagrams_TwoD_Sunburst_sunburstEx.svg#diagram=sunburstEx&width=500>>
sunburst :: (Renderable (Path V2 n) b, TypeableFloat n) => Tree a -> QDiagram b V2 n Any
sunburst = sunburst' def

# LANGUAGE FlexibleContexts      #
# LANGUAGE ScopedTypeVariables   #
# LANGUAGE TemplateHaskell       #
---------------------------------------------------------------------------
 |
 Module      :  Diagrams.TwoD.Sunburst
 License     :  BSD-style (see LICENSE)
 Maintainer  :  

 Generation of Sunburst Partitions. A radial view of a Treemap.

 The partitions are created without examining the contents of the tree nodes
 which allows us to create a sunburst for any @Tree a@. As a consequence we cannot
 base the size or color of the sections on the data in the tree, but only
 on depth and number of children. Of course the code could easily be adapted
 to handle more specific tree data.

 </~john.stasko/papers/ijhcs00.pdf>.

---------------------------------------------------------------------------
  * Sunburst
 ^ Relative width of the sections.
 Section data: Will be stored in nodes of a new rose tree and used to
 make each section of the sunburst partition.
 section radius
 section width
 start direction
 sweep angle
 number of sections
 color
 Make n sections (annular wedges) starting in direction d and sweeping a
 Convert an arbitrary @Tree a@ to a @Tree SData@ storing the sections info
 color of the list is repeated. If the color list is empty, lightgray is used.
 | Take any @Tree a@ and @SunburstOpts@ and make a sunburst partition.
   Basically a treemap with a radial layout.
   The root is the center of the sunburst and its circumference is divided
   evenly according to the number of child nodes it has. Then each of those
   sections is treated the same way.
 | @sunburst@ with default opts

   > import Diagrams.TwoD.Sunburst
   > import Data.Tree (unfoldTree)

   <<diagrams/src_Diagrams_TwoD_Sunburst_sunburstEx.svg#diagram=sunburstEx&width=500>>
# LANGUAGE MultiParamTypeClasses #
# LANGUAGE TypeFamilies           #

  Copyright    :   ( c ) 2013 - 14  
  See   ,   , \"An evaluation of space - filling
  information visualizations for depicting hierarchical structures\ " , 2000 .

module Diagrams.TwoD.Sunburst
    sunburst'
  , sunburst
  , SunburstOpts(..)
  , radius
  , sectionWidth
  , colors
  ) where

import           Data.Default.Class
import qualified Data.Foldable as F
import           Data.Tree
import           Diagrams.Prelude   hiding (radius)

data SunburstOpts n = SunburstOpts
  ^ Relative size of the root circle , usually 1 .
  ^ Color list one for each ring .

instance Fractional n => Default (SunburstOpts n) where
  def = SunburstOpts
    { _radius       = 1.0
    , _sectionWidth = 0.3
    , _colors       = [ lightcoral, lightseagreen, paleturquoise
                      , lightsteelblue, plum, violet, coral, honeydew]}

makeLenses ''SunburstOpts

data SData n = SData

sections :: (Renderable (Path V2 n) b, TypeableFloat n) =>
            SData n -> QDiagram b V2 n Any
sections (SData r s d a n c) = mconcat $ iterateN n (rotate theta) w
  where
    theta = a ^/ fromIntegral n
    w = annularWedge (s + r) r d theta # lc white # lwG 0.008 # fc c

  in the nodes . If color list is shorter than depth of tree than the first
toTree :: Floating n =>
          SunburstOpts n -> Tree a -> Direction V2 n -> Angle n  -> Tree (SData n)
toTree (SunburstOpts r s []) x q1 q2 =
  toTree (SunburstOpts r s (repeat lightgray)) x q1 q2
toTree (SunburstOpts r s (c:cs)) (Node _ ts) d a = Node (SData r s d a n c) ts'
  where
    n = length ts
    dt =  a ^/ fromIntegral n
    qs = [rotate (fromIntegral i *^ dt ) d  | i <- [0..n]]
    fs = toTree (SunburstOpts(r + s) s (cs ++ [c]))
    ts' = zipWith3 fs ts (take (n-1) qs) (repeat dt)

sunburst' :: (Renderable (Path V2 n) b, TypeableFloat n) =>
             SunburstOpts n -> Tree a -> QDiagram b V2 n Any
sunburst' opts t = sunB $ toTree opts t xDir fullTurn
  where sunB (Node sd ts') = sections sd <> F.foldMap sunB ts'

    > aTree = unfoldTree ( \n - > ( 0 , replicate n ( n-1 ) ) ) 6
    > sunburstEx = sunburst aTree # pad 1.1
sunburst :: (Renderable (Path V2 n) b, TypeableFloat n) => Tree a -> QDiagram b V2 n Any
sunburst = sunburst' def
