{-|
Module      : Advent.Visualize
Description : Module for visualizing components of the solutions
Copyright   : (c) Eric Mertens, 2018
License     : ISC
Maintainer  : emertens@gmail.com

Thin wrapper around the JuicyPixels library to make it easy to generate
small animations from solution files.

-}
module Advent.Visualize
  ( Image
  , PixelRGB8(..)

  , writePng
  , writeAnimation
  , generateImage

  , coordImage

  , colorWheel
  ) where

import Advent.Coord
import Codec.Picture
import Data.Word (Word8)

-- | Generate an image given coordinate bounds and a projection from coordinates to colors.
coordImage ::
  Pixel p =>
  (Coord, Coord) {- ^ inclusive coordinate range -} ->
  (Coord -> p)   {- ^ pixel coloring function -} ->
  Image p
coordImage :: forall p. Pixel p => (Coord, Coord) -> (Coord -> p) -> Image p
coordImage (C Int
loy Int
lox, C Int
hiy Int
hix) Coord -> p
f = (Int -> Int -> p) -> Int -> Int -> Image p
forall px. Pixel px => (Int -> Int -> px) -> Int -> Int -> Image px
generateImage Int -> Int -> p
toPixel Int
width Int
height
  where
    toPixel :: Int -> Int -> p
toPixel Int
x Int
y = Coord -> p
f (Int -> Int -> Coord
C (Int
loyInt -> Int -> Int
forall a. Num a => a -> a -> a
+Int
y) (Int
loxInt -> Int -> Int
forall a. Num a => a -> a -> a
+Int
x))
    width :: Int
width       = Int
hix Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
lox Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1
    height :: Int
height      = Int
hiy Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
loy Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1

-- | Assign a gradient rainbow to the values of a 8-bit number.
colorWheel :: Word8 -> PixelRGB8
colorWheel :: Word8 -> PixelRGB8
colorWheel Word8
i
  | Word8
i Word8 -> Word8 -> Bool
forall a. Ord a => a -> a -> Bool
< Word8
85    = Word8 -> Word8 -> Word8 -> PixelRGB8
PixelRGB8 (Word8
255 Word8 -> Word8 -> Word8
forall a. Num a => a -> a -> a
- Word8
i Word8 -> Word8 -> Word8
forall a. Num a => a -> a -> a
* Word8
3) Word8
0 (Word8
i Word8 -> Word8 -> Word8
forall a. Num a => a -> a -> a
* Word8
3)
  | Word8
i Word8 -> Word8 -> Bool
forall a. Ord a => a -> a -> Bool
< Word8
170   = Word8 -> Word8 -> Word8 -> PixelRGB8
PixelRGB8 Word8
0 ((Word8
iWord8 -> Word8 -> Word8
forall a. Num a => a -> a -> a
-Word8
85) Word8 -> Word8 -> Word8
forall a. Num a => a -> a -> a
* Word8
3) (Word8
255 Word8 -> Word8 -> Word8
forall a. Num a => a -> a -> a
- (Word8
iWord8 -> Word8 -> Word8
forall a. Num a => a -> a -> a
-Word8
85)Word8 -> Word8 -> Word8
forall a. Num a => a -> a -> a
*Word8
3)
  | Bool
otherwise = Word8 -> Word8 -> Word8 -> PixelRGB8
PixelRGB8 ((Word8
iWord8 -> Word8 -> Word8
forall a. Num a => a -> a -> a
-Word8
170) Word8 -> Word8 -> Word8
forall a. Num a => a -> a -> a
* Word8
3) (Word8
255 Word8 -> Word8 -> Word8
forall a. Num a => a -> a -> a
- (Word8
iWord8 -> Word8 -> Word8
forall a. Num a => a -> a -> a
-Word8
170)Word8 -> Word8 -> Word8
forall a. Num a => a -> a -> a
*Word8
3) Word8
0

-- | Save a looping animated GIF to disk given the animation frames and a delay.
writeAnimation ::
  FilePath          {- ^ output filename -} ->
  Int               {- ^ frame delay in centiseconds -} ->
  [Image PixelRGB8] {- ^ animation frames -} ->
  IO ()
writeAnimation :: FilePath -> Int -> [Image PixelRGB8] -> IO ()
writeAnimation FilePath
path Int
delay [Image PixelRGB8]
imgs =
  case FilePath
-> Int
-> GifLooping
-> [Image PixelRGB8]
-> Either FilePath (IO ())
writeGifAnimation FilePath
path Int
delay GifLooping
LoopingForever [Image PixelRGB8]
imgs of
    Left FilePath
e -> FilePath -> IO ()
forall a. FilePath -> IO a
forall (m :: * -> *) a. MonadFail m => FilePath -> m a
fail FilePath
e
    Right IO ()
io -> IO ()
io