-
Notifications
You must be signed in to change notification settings - Fork 2
/
Grid.hs
157 lines (139 loc) · 6.02 KB
/
Grid.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
-- |
-- Module : Presentation.Yeamer.Internal.Grid
-- Copyright : (c) Justus Sagemüller 2017
-- License : GPL v3
--
-- Maintainer : (@) jsag $ hvl.no
-- Stability : experimental
-- Portability : portable
--
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE DeriveFoldable #-}
{-# LANGUAGE DeriveTraversable #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TemplateHaskell #-}
module Presentation.Yeamer.Internal.Grid where
import Data.Semigroup.Numbered
import GHC.Generics
import Data.Aeson (FromJSON, ToJSON)
import Flat (Flat)
import Data.Ratio ((%))
import Data.List (sortBy)
import Data.Ord (comparing)
import Control.Applicative (liftA2)
import Control.Monad.Trans.State
import Control.Arrow (second)
import Lens.Micro
import Lens.Micro.TH
data Gridded a = GridRegion a
| GridDivisions [[Gridded a]]
deriving (Generic, Functor, Eq, Show, Foldable, Traversable)
instance FromJSON a => FromJSON (Gridded a)
instance ToJSON a => ToJSON (Gridded a)
instance Flat a => Flat (Gridded a)
instance Applicative Gridded where
pure = GridRegion
fs <*> GridRegion x = ($ x) <$> fs
GridRegion f <*> xs = f <$> xs
GridDivisions fs <*> GridDivisions xs = GridDivisions $ liftA2 (<*>) <$> fs <*> xs
instance Monad Gridded where
return = GridRegion
GridRegion x >>= f = f x
GridDivisions xs >>= f = GridDivisions $ map (>>=f) <$> xs
instance SemigroupNo 0 (Gridded a) where
sappendN _ (GridDivisions g) (GridDivisions h) | length g == length h
= GridDivisions $ zipWith (++) g h
sappendN _ e (GridDivisions [r]) = GridDivisions [e:r]
sappendN _ (GridDivisions [r]) e = GridDivisions [r++[e]]
sappendN p a b = GridDivisions [[a,b]]
instance SemigroupNo 1 (Gridded a) where
sappendN _ (GridDivisions g@(l:_)) (GridDivisions h@(m:_)) | length l == length m
= GridDivisions $ g++h
sappendN _ e (GridDivisions c@([r]:_)) = GridDivisions $ [e]:c
sappendN _ (GridDivisions c@([r]:_)) e = GridDivisions $ c++[[e]]
sappendN p a b = GridDivisions [[a],[b]]
data GridRange = GridRange {
_xBegin, _xEnd, _yBegin, _yEnd :: Int }
deriving (Eq, Show, Generic)
makeLenses ''GridRange
data GridLayout a = GridLayout {
_gridWidth, _gridHeight :: Int
, _gridContents :: [(GridRange, a)]
} deriving (Functor, Generic, Eq, Show)
makeLenses ''GridLayout
layoutGrid :: Gridded a -> GridLayout a
layoutGrid = fmap snd . fst . layoutGridP
type GridRegionId = Int
layoutGridP :: Gridded a -> ( GridLayout (GridRegionId, a)
, [(GridRegionId, b)] -> (Gridded b, [(GridRegionId, b)]) )
layoutGridP = (`evalState`0) . go
where go (GridRegion a) = do
i <- get
put $ i+1
return ( GridLayout 1 1 [(GridRange 0 1 0 1, (i, a))]
, \((_, b):lgrs) -> (GridRegion b, lgrs) )
go (GridDivisions [])
= return ( GridLayout 0 0 []
, \lgrs -> (GridDivisions [], lgrs) )
go (GridDivisions [row]) = do
layouts <- mapM go row
return ( alignLayoutDirectional gridWidth xBegin xEnd
gridHeight yBegin yEnd
(fst<$>layouts)
, let procLgrs [] acc lgrs = (GridDivisions [acc []], lgrs)
procLgrs (srow:srows) acc lgrs
= let (srowRes, lgrs') = srow lgrs
in procLgrs srows (acc . (srowRes:)) lgrs'
in procLgrs (snd<$>layouts) id )
go (GridDivisions rows) = do
rLayouts <- mapM (go . GridDivisions . pure) rows
return ( alignLayoutDirectional gridHeight yBegin yEnd
gridWidth xBegin xEnd
(fst<$>rLayouts)
, let procLgrs [] acc lgrs = (GridDivisions $ acc [], lgrs)
procLgrs (srow:srows) acc lgrs
= let (GridDivisions [srowRes], lgrs') = srow lgrs
in procLgrs srows (acc . (srowRes:)) lgrs'
in procLgrs (snd<$>rLayouts) id )
alignLayoutDirectional
:: Lens' (GridLayout a) Int -> Lens' GridRange Int -> Lens' GridRange Int
-> Lens' (GridLayout a) Int -> Lens' GridRange Int -> Lens' GridRange Int
-> [GridLayout a] -> GridLayout a
alignLayoutDirectional gridLength sBegin sEnd
gridThickness zBegin zEnd
= align . map (\(ζ, h') -> ((0,h'), (h',(ζ,0))))
. xcat 0
where align state = case sortBy (comparing $ snd . fst) state of
(headSnail@((_,ySnail), _) : others)
| ySnail < 1
-> case break ((>ySnail) . snd . fst) others of
(snails, hares)
-> align $
[ ((ySnail, ySnail+h'), (h', (ζ,i+1)))
| (_, (h', (ζ,i))) <- headSnail : snails ]
++ [ ((ySnail,yHare), (h', shiftup cH))
| ((_,yHare), (h', cH)) <- hares ]
_ -> gather $ fst . snd . snd <$> state
shiftup (ζ, i)
= ( ζ & gridThickness %~ (+1)
& gridContents . mapped
%~ \(range, a) -> (range & zBegin%~shift
& zEnd%~shift , a)
, i+1 )
where shift j | j>i = j+1
| otherwise = j
xcat _ [] = []
xcat ix (ζ : cells)
= ( ζ & gridContents . mapped . _1 %~ (sBegin %~(+ix))
. (sEnd %~(+ix))
, 1%(ζ^.gridThickness) )
: xcat (ix + ζ^.gridLength) cells
gather [ζ] = ζ
gather (ζ₀ : others) = case gather others of
ζo | ζ₀^.gridThickness == ζo^.gridThickness
-> ζo & gridLength %~ (ζ₀^.gridLength +)
& gridContents %~ (ζ₀^.gridContents ++)