{-# LANGUAGE DeriveGeneric #-}

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

-- Screen datatypes and functions

-- 2017 Francesco Ariis GPLv3

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


-- a canvas where to draw our stuff


module Terminal.Game.Plane where

import Terminal.Game.Character

import qualified Data.Array as A
import qualified Data.Bifunctor as B
import qualified Data.Colour.RGBSpace as S
import qualified Data.List.Split as LS
import qualified Data.Tuple as T
import qualified Data.Word as W
import qualified GHC.Generics as G
import qualified System.Console.ANSI as CA


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

-- DATA TYPES --

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


-- | 'Row's and 'Column's are 1-based (top-left position is @1 1@).

type Coords = (Row, Column)
type Row    = Int
type Column = Int

-- | Size of a surface in 'Row's and 'Column's.

type Dimensions = (Width, Height)

-- | Expressed in 'Column's.

type Width  = Int
-- | Expressed in 'Row's.

type Height = Int

type Bold     = Bool
type Reversed = Bool

data ColorInfo = ANSIColorInfo (CA.Color, CA.ColorIntensity)
               | RGBColorInfo (S.Colour Float)
               | PaletteColorInfo W.Word8
               deriving (Row -> ColorInfo -> ShowS
[ColorInfo] -> ShowS
ColorInfo -> String
(Row -> ColorInfo -> ShowS)
-> (ColorInfo -> String)
-> ([ColorInfo] -> ShowS)
-> Show ColorInfo
forall a.
(Row -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Row -> ColorInfo -> ShowS
showsPrec :: Row -> ColorInfo -> ShowS
$cshow :: ColorInfo -> String
show :: ColorInfo -> String
$cshowList :: [ColorInfo] -> ShowS
showList :: [ColorInfo] -> ShowS
Show, ColorInfo -> ColorInfo -> Bool
(ColorInfo -> ColorInfo -> Bool)
-> (ColorInfo -> ColorInfo -> Bool) -> Eq ColorInfo
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: ColorInfo -> ColorInfo -> Bool
== :: ColorInfo -> ColorInfo -> Bool
$c/= :: ColorInfo -> ColorInfo -> Bool
/= :: ColorInfo -> ColorInfo -> Bool
Eq)

-- can be an ASCIIChar or a special, transparent character

data Cell = CellChar Char Bold Reversed (Maybe ColorInfo)
          | Transparent
          deriving (Row -> Cell -> ShowS
[Cell] -> ShowS
Cell -> String
(Row -> Cell -> ShowS)
-> (Cell -> String) -> ([Cell] -> ShowS) -> Show Cell
forall a.
(Row -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Row -> Cell -> ShowS
showsPrec :: Row -> Cell -> ShowS
$cshow :: Cell -> String
show :: Cell -> String
$cshowList :: [Cell] -> ShowS
showList :: [Cell] -> ShowS
Show, Cell -> Cell -> Bool
(Cell -> Cell -> Bool) -> (Cell -> Cell -> Bool) -> Eq Cell
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: Cell -> Cell -> Bool
== :: Cell -> Cell -> Bool
$c/= :: Cell -> Cell -> Bool
/= :: Cell -> Cell -> Bool
Eq, (forall x. Cell -> Rep Cell x)
-> (forall x. Rep Cell x -> Cell) -> Generic Cell
forall x. Rep Cell x -> Cell
forall x. Cell -> Rep Cell x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. Cell -> Rep Cell x
from :: forall x. Cell -> Rep Cell x
$cto :: forall x. Rep Cell x -> Cell
to :: forall x. Rep Cell x -> Cell
G.Generic)
        -- I found no meaningful speed improvements by making this

        -- only w/ 1 constructor.


-- | A two-dimensional surface (Row, Column) where to blit stuff.

newtype Plane = Plane { Plane -> Array Coords Cell
fromPlane :: A.Array Coords Cell }
              deriving (Row -> Plane -> ShowS
[Plane] -> ShowS
Plane -> String
(Row -> Plane -> ShowS)
-> (Plane -> String) -> ([Plane] -> ShowS) -> Show Plane
forall a.
(Row -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Row -> Plane -> ShowS
showsPrec :: Row -> Plane -> ShowS
$cshow :: Plane -> String
show :: Plane -> String
$cshowList :: [Plane] -> ShowS
showList :: [Plane] -> ShowS
Show, Plane -> Plane -> Bool
(Plane -> Plane -> Bool) -> (Plane -> Plane -> Bool) -> Eq Plane
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: Plane -> Plane -> Bool
== :: Plane -> Plane -> Bool
$c/= :: Plane -> Plane -> Bool
/= :: Plane -> Plane -> Bool
Eq, (forall x. Plane -> Rep Plane x)
-> (forall x. Rep Plane x -> Plane) -> Generic Plane
forall x. Rep Plane x -> Plane
forall x. Plane -> Rep Plane x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. Plane -> Rep Plane x
from :: forall x. Plane -> Rep Plane x
$cto :: forall x. Rep Plane x -> Plane
to :: forall x. Rep Plane x -> Plane
G.Generic)
        -- Could this be made into an UArray? Nope, since UArray is

        -- only instanced on Words, Int, Chars, etc.


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

-- Plane interface (abstracting Array)


listPlane :: Coords -> [Cell] -> Plane
listPlane :: Coords -> [Cell] -> Plane
listPlane (Row
r, Row
c) [Cell]
cs = Array Coords Cell -> Plane
Plane (Array Coords Cell -> Plane) -> Array Coords Cell -> Plane
forall a b. (a -> b) -> a -> b
$ (Coords, Coords) -> [Cell] -> Array Coords Cell
forall i e. Ix i => (i, i) -> [e] -> Array i e
A.listArray ((Row
1,Row
1), (Row
r, Row
c)) [Cell]
cs

-- | Dimensions or a plane.

planeSize :: Plane -> Dimensions
planeSize :: Plane -> Coords
planeSize Plane
p = Coords -> Coords
forall a b. (a, b) -> (b, a)
T.swap (Coords -> Coords)
-> ((Coords, Coords) -> Coords) -> (Coords, Coords) -> Coords
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Coords, Coords) -> Coords
forall a b. (a, b) -> b
snd ((Coords, Coords) -> Coords) -> (Coords, Coords) -> Coords
forall a b. (a -> b) -> a -> b
$ Array Coords Cell -> (Coords, Coords)
forall i e. Array i e -> (i, i)
A.bounds (Plane -> Array Coords Cell
fromPlane Plane
p)

assocsPlane :: Plane -> [(Coords, Cell)]
assocsPlane :: Plane -> [(Coords, Cell)]
assocsPlane Plane
p = Array Coords Cell -> [(Coords, Cell)]
forall i e. Ix i => Array i e -> [(i, e)]
A.assocs (Plane -> Array Coords Cell
fromPlane Plane
p)

elemsPlane :: Plane -> [Cell]
elemsPlane :: Plane -> [Cell]
elemsPlane Plane
p = Array Coords Cell -> [Cell]
forall i e. Array i e -> [e]
A.elems (Plane -> Array Coords Cell
fromPlane Plane
p)

-- Array.//

updatePlane :: Plane -> [(Coords, Cell)] -> Plane
updatePlane :: Plane -> [(Coords, Cell)] -> Plane
updatePlane (Plane Array Coords Cell
a) [(Coords, Cell)]
kcs = Array Coords Cell -> Plane
Plane (Array Coords Cell -> Plane) -> Array Coords Cell -> Plane
forall a b. (a -> b) -> a -> b
$ Array Coords Cell
a Array Coords Cell -> [(Coords, Cell)] -> Array Coords Cell
forall i e. Ix i => Array i e -> [(i, e)] -> Array i e
A.// [(Coords, Cell)]
kcs

-- faux map

mapPlane :: (Cell -> Cell) -> Plane -> Plane
mapPlane :: (Cell -> Cell) -> Plane -> Plane
mapPlane Cell -> Cell
f (Plane Array Coords Cell
a) = Array Coords Cell -> Plane
Plane (Array Coords Cell -> Plane) -> Array Coords Cell -> Plane
forall a b. (a -> b) -> a -> b
$ (Cell -> Cell) -> Array Coords Cell -> Array Coords Cell
forall a b. (a -> b) -> Array Coords a -> Array Coords b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Cell -> Cell
f Array Coords Cell
a


----------

-- CREA --

----------


creaCell :: Char -> Cell
creaCell :: Char -> Cell
creaCell Char
ch = Char -> Bool -> Bool -> Maybe ColorInfo -> Cell
CellChar Char
chm Bool
False Bool
False Maybe ColorInfo
forall a. Maybe a
Nothing
    where
          chm :: Char
chm = Char -> Char
win32SafeChar Char
ch

colorCell :: CA.Color -> CA.ColorIntensity -> Cell -> Cell
colorCell :: Color -> ColorIntensity -> Cell -> Cell
colorCell Color
k ColorIntensity
i (CellChar Char
c Bool
b Bool
r Maybe ColorInfo
_) = Char -> Bool -> Bool -> Maybe ColorInfo -> Cell
CellChar Char
c Bool
b Bool
r (ColorInfo -> Maybe ColorInfo
forall a. a -> Maybe a
Just (ColorInfo -> Maybe ColorInfo) -> ColorInfo -> Maybe ColorInfo
forall a b. (a -> b) -> a -> b
$ (Color, ColorIntensity) -> ColorInfo
ANSIColorInfo (Color
k, ColorIntensity
i))
colorCell Color
_ ColorIntensity
_ Cell
Transparent        = Cell
Transparent

rgbColorCell :: S.Colour Float -> Cell -> Cell
rgbColorCell :: Colour Float -> Cell -> Cell
rgbColorCell Colour Float
k (CellChar Char
c Bool
b Bool
r Maybe ColorInfo
_) = Char -> Bool -> Bool -> Maybe ColorInfo -> Cell
CellChar Char
c Bool
b Bool
r (ColorInfo -> Maybe ColorInfo
forall a. a -> Maybe a
Just (ColorInfo -> Maybe ColorInfo) -> ColorInfo -> Maybe ColorInfo
forall a b. (a -> b) -> a -> b
$ Colour Float -> ColorInfo
RGBColorInfo Colour Float
k)
rgbColorCell Colour Float
_ Cell
Transparent        = Cell
Transparent

paletteColorCell :: W.Word8 -> Cell -> Cell
paletteColorCell :: Word8 -> Cell -> Cell
paletteColorCell Word8
k (CellChar Char
c Bool
b Bool
r Maybe ColorInfo
_) = Char -> Bool -> Bool -> Maybe ColorInfo -> Cell
CellChar Char
c Bool
b Bool
r (ColorInfo -> Maybe ColorInfo
forall a. a -> Maybe a
Just (ColorInfo -> Maybe ColorInfo) -> ColorInfo -> Maybe ColorInfo
forall a b. (a -> b) -> a -> b
$ Word8 -> ColorInfo
PaletteColorInfo Word8
k)
paletteColorCell Word8
_ Cell
Transparent        = Cell
Transparent

boldCell :: Cell -> Cell
boldCell :: Cell -> Cell
boldCell (CellChar Char
c Bool
_ Bool
r Maybe ColorInfo
k) = Char -> Bool -> Bool -> Maybe ColorInfo -> Cell
CellChar Char
c Bool
True Bool
r Maybe ColorInfo
k
boldCell Cell
Transparent        = Cell
Transparent

reverseCell :: Cell -> Cell
reverseCell :: Cell -> Cell
reverseCell (CellChar Char
c Bool
b Bool
_ Maybe ColorInfo
k) = Char -> Bool -> Bool -> Maybe ColorInfo -> Cell
CellChar Char
c Bool
b Bool
True Maybe ColorInfo
k
reverseCell Cell
Transparent        = Cell
Transparent

-- | Creates 'Plane' from 'String', good way to import ASCII

-- art/diagrams. Returns a 1×1 transparent plane on empty string.

stringPlane :: String -> Plane
stringPlane :: String -> Plane
stringPlane String
t = Maybe Char -> String -> Plane
stringPlaneGeneric Maybe Char
forall a. Maybe a
Nothing String
t

-- | Same as 'stringPlane', but with transparent 'Char'.

-- Returns a 1×1 transparent plane on empty string.

stringPlaneTrans :: Char -> String -> Plane
stringPlaneTrans :: Char -> String -> Plane
stringPlaneTrans Char
c String
t = Maybe Char -> String -> Plane
stringPlaneGeneric (Char -> Maybe Char
forall a. a -> Maybe a
Just Char
c) String
t

-- | Creates an empty, opaque 'Plane'.

blankPlane :: Width -> Height -> Plane
blankPlane :: Row -> Row -> Plane
blankPlane Row
w Row
h = Coords -> [Cell] -> Plane
listPlane (Row
h, Row
w) (Cell -> [Cell]
forall a. a -> [a]
repeat (Cell -> [Cell]) -> Cell -> [Cell]
forall a b. (a -> b) -> a -> b
$ Char -> Cell
creaCell Char
' ')

-- | Adds transparency to a plane, matching a given character

makeTransparent :: Char -> Plane -> Plane
makeTransparent :: Char -> Plane -> Plane
makeTransparent Char
tc Plane
p = (Cell -> Cell) -> Plane -> Plane
mapPlane Cell -> Cell
f Plane
p
    where
          f :: Cell -> Cell
f Cell
cl | Cell -> Char
cellChar Cell
cl Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
tc = Cell
Transparent
               | Bool
otherwise         = Cell
cl

-- | Changes every transparent cell in the 'Plane' to an opaque @' '@

-- character.

makeOpaque :: Plane -> Plane
makeOpaque :: Plane -> Plane
makeOpaque Plane
p = let (Row
w, Row
h) = Plane -> Coords
planeSize Plane
p
               in Plane -> Plane -> Coords -> Plane
pastePlane Plane
p (Row -> Row -> Plane
blankPlane Row
w Row
h) (Row
1, Row
1)



-----------

-- SLICE --

-----------


-- | Paste one plane over the other at a certain position (p1 gets over p2).

pastePlane :: Plane -> Plane -> Coords -> Plane
pastePlane :: Plane -> Plane -> Coords -> Plane
pastePlane Plane
p1 Plane
p2 (Row
r, Row
c)
            | Row
r Row -> Row -> Bool
forall a. Ord a => a -> a -> Bool
> Row
h2 Bool -> Bool -> Bool
|| Row
c Row -> Row -> Bool
forall a. Ord a => a -> a -> Bool
> Row
w2 = Plane
p2
            | Bool
otherwise =
                let ks :: [(Coords, Cell)]
ks = Plane -> [(Coords, Cell)]
assocsPlane Plane
p1
                    fs :: [(Coords, Cell)]
fs = ((Coords, Cell) -> Bool) -> [(Coords, Cell)] -> [(Coords, Cell)]
forall a. (a -> Bool) -> [a] -> [a]
filter (\(Coords, Cell)
x -> (Coords, Cell) -> Bool
forall {a}. (a, Cell) -> Bool
solid (Coords, Cell)
x Bool -> Bool -> Bool
&& (Coords, Cell) -> Bool
forall {b}. (Coords, b) -> Bool
inside (Coords, Cell)
x) [(Coords, Cell)]
ks
                    ts :: [(Coords, Cell)]
ts = ((Coords, Cell) -> (Coords, Cell))
-> [(Coords, Cell)] -> [(Coords, Cell)]
forall a b. (a -> b) -> [a] -> [b]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((Coords -> Coords) -> (Coords, Cell) -> (Coords, Cell)
forall a b c. (a -> b) -> (a, c) -> (b, c)
forall (p :: * -> * -> *) a b c.
Bifunctor p =>
(a -> b) -> p a c -> p b c
B.first Coords -> Coords
trasl) [(Coords, Cell)]
fs
                in Plane -> [(Coords, Cell)] -> Plane
updatePlane Plane
p2 [(Coords, Cell)]
ts
    where
          trasl :: Coords -> Coords
          trasl :: Coords -> Coords
trasl (Row
wr, Row
wc) = (Row
wr Row -> Row -> Row
forall a. Num a => a -> a -> a
+ Row
r Row -> Row -> Row
forall a. Num a => a -> a -> a
- Row
1, Row
wc Row -> Row -> Row
forall a. Num a => a -> a -> a
+ Row
c Row -> Row -> Row
forall a. Num a => a -> a -> a
- Row
1)

          -- inside new position, cheaper than first mapping and then

          -- filtering.

          inside :: (Coords, b) -> Bool
inside (Coords
wcs, b
_) =
                let (Row
r1', Row
c1') = Coords -> Coords
trasl Coords
wcs
                in Row
r1' Row -> Row -> Bool
forall a. Ord a => a -> a -> Bool
>= Row
1 Bool -> Bool -> Bool
&& Row
r1' Row -> Row -> Bool
forall a. Ord a => a -> a -> Bool
<= Row
h2 Bool -> Bool -> Bool
&&
                   Row
c1' Row -> Row -> Bool
forall a. Ord a => a -> a -> Bool
>= Row
1 Bool -> Bool -> Bool
&& Row
c1' Row -> Row -> Bool
forall a. Ord a => a -> a -> Bool
<= Row
w2

          solid :: (a, Cell) -> Bool
solid (a
_, Cell
Transparent) = Bool
False
          solid (a, Cell)
_                = Bool
True

          (Row
w2, Row
h2)  = Plane -> Coords
planeSize Plane
p2

-- | Cut out a plane by top-left and bottom-right coordinates.

-- Returns a 1×1 transparent plane when @r1>r2@ or @c1>c2@.

subPlane :: Plane -> Coords -> Coords -> Plane
subPlane :: Plane -> Coords -> Coords -> Plane
subPlane Plane
p (Row
r1, Row
c1) (Row
r2, Row
c2)
        | Row
r1 Row -> Row -> Bool
forall a. Ord a => a -> a -> Bool
> Row
r2 Bool -> Bool -> Bool
|| Row
c1 Row -> Row -> Bool
forall a. Ord a => a -> a -> Bool
> Row
c2 = Char -> Plane -> Plane
makeTransparent Char
' ' (Row -> Row -> Plane
blankPlane Row
1 Row
1)
        | Bool
otherwise          =
            let cs :: [(Coords, Cell)]
cs       = Plane -> [(Coords, Cell)]
assocsPlane Plane
p
                fs :: [(Coords, Cell)]
fs       = ((Coords, Cell) -> Bool) -> [(Coords, Cell)] -> [(Coords, Cell)]
forall a. (a -> Bool) -> [a] -> [a]
filter (Coords, Cell) -> Bool
forall {b}. (Coords, b) -> Bool
f [(Coords, Cell)]
cs
                (Row
pw, Row
ph) = Plane -> Coords
planeSize Plane
p
                (Row
w, Row
h)   = (Row -> Row -> Row
forall a. Ord a => a -> a -> a
min Row
pw (Row
c2Row -> Row -> Row
forall a. Num a => a -> a -> a
-Row
c1Row -> Row -> Row
forall a. Num a => a -> a -> a
+Row
1), Row -> Row -> Row
forall a. Ord a => a -> a -> a
min Row
ph (Row
r2Row -> Row -> Row
forall a. Num a => a -> a -> a
-Row
r1Row -> Row -> Row
forall a. Num a => a -> a -> a
+Row
1))
            in Coords -> [Cell] -> Plane
listPlane (Row
h, Row
w) (((Coords, Cell) -> Cell) -> [(Coords, Cell)] -> [Cell]
forall a b. (a -> b) -> [a] -> [b]
map (Coords, Cell) -> Cell
forall a b. (a, b) -> b
snd [(Coords, Cell)]
fs)
    where
          f :: (Coords, b) -> Bool
f ((Row
rw, Row
cw), b
_) = Row
rw Row -> Row -> Bool
forall a. Ord a => a -> a -> Bool
>= Row
r1 Bool -> Bool -> Bool
&& Row
rw Row -> Row -> Bool
forall a. Ord a => a -> a -> Bool
<= Row
r2 Bool -> Bool -> Bool
&&
                            Row
cw Row -> Row -> Bool
forall a. Ord a => a -> a -> Bool
>= Row
c1 Bool -> Bool -> Bool
&& Row
cw Row -> Row -> Bool
forall a. Ord a => a -> a -> Bool
<= Row
c2

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

-- INQUIRE --

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


cellChar :: Cell -> Char
cellChar :: Cell -> Char
cellChar (CellChar Char
c Bool
_ Bool
_ Maybe ColorInfo
_) = Char
c
cellChar Cell
Transparent        = Char
' '

cellColor :: Cell -> Maybe ColorInfo
cellColor :: Cell -> Maybe ColorInfo
cellColor (CellChar Char
_ Bool
_ Bool
_ Maybe ColorInfo
k) = Maybe ColorInfo
k
cellColor Cell
Transparent        = Maybe ColorInfo
forall a. Maybe a
Nothing

isBold :: Cell -> Bool
isBold :: Cell -> Bool
isBold (CellChar Char
_ Bool
b Bool
_ Maybe ColorInfo
_) = Bool
b
isBold Cell
_                  = Bool
False

isReversed :: Cell -> Bool
isReversed :: Cell -> Bool
isReversed (CellChar Char
_ Bool
_ Bool
r Maybe ColorInfo
_) = Bool
r
isReversed Cell
_                  = Bool
False

-- | A String (@\\n@ divided and ended) representing the 'Plane'. Useful

-- for debugging/testing purposes.

planePaper :: Plane -> String
planePaper :: Plane -> String
planePaper Plane
p = [String] -> String
unlines ([String] -> String) -> ([Cell] -> [String]) -> [Cell] -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Row -> String -> [String]
forall e. Row -> [e] -> [[e]]
LS.chunksOf Row
w (String -> [String]) -> ([Cell] -> String) -> [Cell] -> [String]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Cell -> Char) -> [Cell] -> String
forall a b. (a -> b) -> [a] -> [b]
map Cell -> Char
cellChar ([Cell] -> String) -> [Cell] -> String
forall a b. (a -> b) -> a -> b
$ Plane -> [Cell]
elemsPlane Plane
p
    where
          w :: Int
          w :: Row
w = Row -> Row
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Row -> Row) -> (Plane -> Row) -> Plane -> Row
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Coords -> Row
forall a b. (a, b) -> a
fst (Coords -> Row) -> (Plane -> Coords) -> Plane -> Row
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Plane -> Coords
planeSize (Plane -> Row) -> Plane -> Row
forall a b. (a -> b) -> a -> b
$ Plane
p

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

-- ANCILLARIES --

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


stringPlaneGeneric :: Maybe Char -> String -> Plane
stringPlaneGeneric :: Maybe Char -> String -> Plane
stringPlaneGeneric Maybe Char
_ String
"" = Char -> Plane -> Plane
makeTransparent Char
' ' (Row -> Row -> Plane
blankPlane Row
1 Row
1)
stringPlaneGeneric Maybe Char
mc String
t = Plane
vitrous
    where
          lined :: [String]
lined = String -> [String]
lines String
t

          h :: Int
          h :: Row
h = [String] -> Row
forall a. [a] -> Row
forall (t :: * -> *) a. Foldable t => t a -> Row
length [String]
lined

          w :: Int
          w :: Row
w = [Row] -> Row
forall a. Ord a => [a] -> a
forall (t :: * -> *) a. (Foldable t, Ord a) => t a -> a
maximum ((String -> Row) -> [String] -> [Row]
forall a b. (a -> b) -> [a] -> [b]
map String -> Row
forall a. [a] -> Row
forall (t :: * -> *) a. Foldable t => t a -> Row
length [String]
lined)

          pad :: Int -> String -> String
          pad :: Row -> ShowS
pad Row
mw String
tl = Row -> ShowS
forall a. Row -> [a] -> [a]
take Row
mw (String
tl String -> ShowS
forall a. [a] -> [a] -> [a]
++ Char -> String
forall a. a -> [a]
repeat Char
' ')

          padded :: [String]
          padded :: [String]
padded = ShowS -> [String] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map (Row -> ShowS
pad Row
w) [String]
lined

          celled :: [Cell]
          celled :: [Cell]
celled = (Char -> Cell) -> String -> [Cell]
forall a b. (a -> b) -> [a] -> [b]
map Char -> Cell
creaCell (String -> [Cell]) -> ([String] -> String) -> [String] -> [Cell]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [String] -> String
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat ([String] -> [Cell]) -> [String] -> [Cell]
forall a b. (a -> b) -> a -> b
$ [String]
padded

          plane :: Plane
          plane :: Plane
plane = Coords -> [Cell] -> Plane
listPlane (Row
h, Row
w) [Cell]
celled

          vitrous :: Plane
          vitrous :: Plane
vitrous = case Maybe Char
mc of
                      Just Char
c  -> Char -> Plane -> Plane
makeTransparent Char
c Plane
plane
                      Maybe Char
Nothing -> Plane
plane