content: {-# LANGUAGE OverloadedStrings #-}
  {-|
    [Blender](https://www.blender.org/) is a free and open-source 3D graphics toolkit.
    It is usually used through a graphical user-interface but can also be
    scripted via Python. These Blender scripts can access 100% of Blender's
    functionality and offer a convenient way of coding 3D effects.
  
    Running Blender can be time-consuming but heavy caching means scripts
    are only run when they change.
  
    Blender cheatsheet:
  
  > # To generate with a transparent background, set film_transparent = True:
  > bpy.context.scene.render.film_transparent = True
  >
  > # Filmic is great for photorealism but bad for animations.
  > # If you want your textures to keep their exact color values,
  > # set the view_transform to 'Standard':
  > bpy.context.scene.view_settings.view_transform = 'Standard'
  >
  > # Blender's default render engine is 'EEVEE', fast but not a raytracer.
  > # To switch to raytracing, set the engine to 'CYCLES':
  > bpy.context.scene.render.engine = 'CYCLES'
  >
  > # Rendering at full resolution can be slow. When developing, try
  > # decreasing the resolution_percentage for faster renders.
  > bpy.context.scene.render.resolution_percentage = 10
  >
  > # The resolution of the final image are set by resolution_x and resolution_y:
  > bpy.context.scene.render.resolution_x = 320
  > bpy.context.scene.render.resolution_y = 180
  
  -}
  module Reanimate.Blender
    ( blender
    , blender'
    ) where
  
  import           Data.Hashable              (Hashable (hash))
  import           Data.Text                  (Text)
  import qualified Data.Text.IO               as T
  import           Graphics.SvgTree           (Tree)
  import           Reanimate.Animation        (SVG)
  import           Reanimate.Cache            (cacheFile, encodeInt)
  import           Reanimate.Constants        (screenHeight, screenWidth)
  import           Reanimate.Misc             (requireExecutable, runCmd)
  import           Reanimate.Parameters       (pNoExternals)
  import           Reanimate.Raster           (mkImage)
  import           Reanimate.Svg.Constructors (mkText)
  import           System.FilePath            (replaceExtension, (<.>))
  import           System.IO.Unsafe           (unsafePerformIO)
  
  -- | Run a Blender script and embed the resulting image file. The
  --   image will be scaled to fit the screen exactly (assuming a default
  --   canvas layout). Note that Blender resolution defaults to 1920x1080
  --   but can be changed in the script code.
  blender :: Text -> SVG
  blender script =
    unsafePerformIO $ mkBlenderImage script
  
  -- | Generate Blender image as a separate PNG file. Can be embedded with
  --   `mkImage`.
  blender' :: Text -> FilePath
  blender' script =
    unsafePerformIO $ mkBlenderImage' script
  
  mkBlenderImage :: Text -> IO Tree
  mkBlenderImage script | pNoExternals = pure $ mkText script
  mkBlenderImage script =
    mkImage screenWidth screenHeight <$> mkBlenderImage' script
  
  mkBlenderImage' :: Text -> IO FilePath
  mkBlenderImage' _ | pNoExternals = pure "/blender/has/been/disabled"
  mkBlenderImage' script = cacheFile template $ \target -> do
      exec <- requireExecutable "blender"
      let py_file = replaceExtension target "py"
      T.writeFile py_file script
      runCmd exec [ "--background","--render-format", "PNG"
                  , "--python-exit-code", "1"
                  , "--render-output", target, "--python", py_file]
    where
      template = encodeInt (hash script) <.> "png"
  