-- | Drawing plane partitions.
--
-- For example:
--
-- > drawPlanePartition3D $ 
-- >   PlanePart [ [5,4,3,3,1]
-- >             , [4,4,2,1]
-- >             , [3,2]
-- >             , [2,1]
-- >             , [1]
-- >             , [1]
-- >             ]
-- 
-- produces the picture:
--
-- <<svg/plane_partition.svg>>
--

{-# LANGUAGE FlexibleContexts, TypeFamilies #-}
module Math.Combinat.Diagrams.Partitions.Plane where

--------------------------------------------------------------------------------

import Math.Combinat.Partitions.Integer
import Math.Combinat.Partitions.Plane

import Math.Combinat.Diagrams.Tableaux as Tableaux

import Linear.Vector
import Linear.Affine

import Data.Colour
import Diagrams.Core
import Diagrams.Prelude
import Diagrams.TwoD.Text

--------------------------------------------------------------------------------

drawPlanePartition3D :: (Renderable (Path V2 Double) b) => PlanePart -> QDiagram b V2 Double Any
drawPlanePartition3D = drawPlanePartition3D' (cadetblue,indianred,lawngreen)

-- | Draws 3D-like (but in fact 2D) diagram of a plane partition, coloring the faces with the given colors
--
drawPlanePartition3D' 
  :: (Renderable (Path V2 Double) b) 
  => (Colour Double, Colour Double, Colour Double) 
  -> PlanePart -> QDiagram b V2 Double Any
drawPlanePartition3D' (col1,col2,col3) pp@(PlanePart pps) = final where

  final  =  leftSides  # fc col1 # lwL linewidth
         <> rightSides # fc col2 # lwL linewidth
         <> topSides   # fc col3 # lwL linewidth

  layers = planePartLayers pp

  linewidth = 0.05 :: Double

  dir_top   = unitY
  dir_left  = fromDirection (angleDir $  210  @@ deg)
  dir_right = fromDirection (angleDir $ (-30) @@ deg)

  ndir_top   = negated dir_top
  ndir_left  = negated dir_left
  ndir_right = negated dir_right

  leftSides  = mconcat $ zipWith lefts  [0..] layers
  rightSides = mconcat $ zipWith rights [0..] layers
 
  topSides   = mconcat $ map tops [1..planePartZHeight pp]

  iscale i v = if i /= 0 then scale (fromIntegral i) v else zero

  tr :: (Transformable t, V t ~ V2, N t ~ Double) => Int -> Int -> Int -> t -> t
  tr i j k = translate ( iscale i dir_right ^+^
                         iscale j dir_left  ^+^
                         iscale k dir_top   )

  rights h (Partition ps) = mconcat [ tr p i h rightRect | (p,i) <- zip ps [0..]                  ]
  lefts  h (Partition ps) = mconcat [ tr j q h leftRect  | (j,q) <- zip [0..] (_dualPartition ps) ]

  tops h = mconcat [ tr j i h topRect | (i,ps) <- (zip [0..] pps) , (j,k) <- (zip [0..] ps) , k==h ]
  
  rightRect = strokeTrail $ glueTrail $ trailFromOffsets [ dir_top  , dir_left  , ndir_top  , ndir_left  ]
  leftRect  = strokeTrail $ glueTrail $ trailFromOffsets [ dir_top  , dir_right , ndir_top  , ndir_right ]
  topRect   = strokeTrail $ glueTrail $ trailFromOffsets [ dir_left , dir_right , ndir_left , ndir_right ]

--------------------------------------------------------------------------------

-- | Draws a plane partitions as a tablaeux, with numbers indicating the Z height
drawPlanePartition2D :: (Renderable (Path V2 Double) b, Renderable (Text Double) b) => PlanePart -> QDiagram b V2 Double Any
drawPlanePartition2D = Tableaux.drawTableau . fromPlanePart

--------------------------------------------------------------------------------