{-|
Module      : Reanimate.Builtin.CirclePlot
Copyright   : Written by David Himmelstrup
License     : Unlicense
Maintainer  : lemmih@gmail.com
Stability   : experimental
Portability : POSIX

Convenience module for rendering circle plots.

-}
module Reanimate.Builtin.CirclePlot
  ( circlePlot
  ) where

import           Codec.Picture       (PixelRGBA8 (..), generateImage)
import           Graphics.SvgTree    (Tree)
import           Reanimate.Constants (screenHeight)
import           Reanimate.Raster    (embedImage)
import           Reanimate.Svg       (flipYAxis, scaleToHeight)

-- | Circle plots are scaled to 'screenHeight'.
--
--   Example:
--
-- @
-- 'circlePlot' 500 $ \\ang r ->
--   'Codec.Picture.Types.promotePixel' $ toRGB8 $ uncurryRGB sRGB $ hsv (ang/pi*180) r 1
-- @
--
--   <<docs/gifs/doc_circlePlot.gif>>
circlePlot :: Int -- ^ Number of diagonal pixels. Only affects quality, not size.
           -> (Double -> Double -> PixelRGBA8)
              -- ^ Angle and radius in radians and percent respectively.
           -> Tree
circlePlot :: Int -> (Double -> Double -> PixelRGBA8) -> Tree
circlePlot Int
density Double -> Double -> PixelRGBA8
fn =
    Double -> Tree -> Tree
scaleToHeight Double
forall a. Fractional a => a
screenHeight (Tree -> Tree) -> Tree -> Tree
forall a b. (a -> b) -> a -> b
$ Tree -> Tree
flipYAxis (Tree -> Tree) -> Tree -> Tree
forall a b. (a -> b) -> a -> b
$
    Image PixelRGBA8 -> Tree
forall a. PngSavable a => Image a -> Tree
embedImage (Image PixelRGBA8 -> Tree) -> Image PixelRGBA8 -> Tree
forall a b. (a -> b) -> a -> b
$ (Int -> Int -> PixelRGBA8) -> Int -> Int -> Image PixelRGBA8
forall px. Pixel px => (Int -> Int -> px) -> Int -> Int -> Image px
generateImage Int -> Int -> PixelRGBA8
forall a a. (Integral a, Integral a) => a -> a -> PixelRGBA8
gen Int
density Int
density
  where
    cN :: Double
cN = Int -> Double
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int -> Double) -> Int -> Double
forall a b. (a -> b) -> a -> b
$ Int
density Int -> Int -> Int
forall a. Integral a => a -> a -> a
`div` Int
2 Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1
    gen :: a -> a -> PixelRGBA8
gen a
x a
y =
      let radius :: Double
radius = Double -> Double
forall a. Floating a => a -> a
sqrt ((a -> Double
forall a b. (Integral a, Num b) => a -> b
fromIntegral a
xDouble -> Double -> Double
forall a. Num a => a -> a -> a
-Double
cN)Double -> Double -> Double
forall a. Floating a => a -> a -> a
**Double
2 Double -> Double -> Double
forall a. Num a => a -> a -> a
+ (a -> Double
forall a b. (Integral a, Num b) => a -> b
fromIntegral a
yDouble -> Double -> Double
forall a. Num a => a -> a -> a
-Double
cN)Double -> Double -> Double
forall a. Floating a => a -> a -> a
**Double
2)
          ang :: Double
ang = Double -> Double -> Double
forall a. RealFloat a => a -> a -> a
atan2 (a -> Double
forall a b. (Integral a, Num b) => a -> b
fromIntegral a
yDouble -> Double -> Double
forall a. Num a => a -> a -> a
-Double
cN) (a -> Double
forall a b. (Integral a, Num b) => a -> b
fromIntegral a
xDouble -> Double -> Double
forall a. Num a => a -> a -> a
-Double
cN)
      in if Double
radius Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
> Double
cN
        then Pixel8 -> Pixel8 -> Pixel8 -> Pixel8 -> PixelRGBA8
PixelRGBA8 Pixel8
0 Pixel8
0 Pixel8
0 Pixel8
0
        else Double -> Double -> PixelRGBA8
fn Double
ang (Double
radius Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Double
cN)