never executed always true always false
1 module HelVM.HelMA.Automata.Piet.Compiler (
2 compile,
3 label4,
4 label4With,
5 ) where
6
7 import HelVM.HelMA.Automata.Piet.Types.Color
8 import HelVM.HelMA.Automata.Piet.Types.Coordinates
9 import HelVM.HelMA.Automata.Piet.Types.Image
10 import HelVM.HelMA.Automata.Piet.Types.Label
11 import HelVM.HelMA.Automata.Piet.Types.Program
12
13
14 import Data.IntMap hiding (filter)
15
16 import qualified Relude.Extra as Extra
17
18 compile :: Image Color -> Program
19 compile image_ = Program image_ mask__ info_ where
20 (mask__, info_) = label4 image_
21
22 data LabellingStatus = LabellingStatus
23 { _currentCoords :: Coordinates
24 , _nextKey :: LabelKey
25 , _mask :: Image LabelKey
26 , _infoMap :: IntMap (Maybe LabelInfo)
27 , _equivalences :: EquivalenceMap
28 } deriving stock (Show)
29
30 label4 :: Eq a => Image a -> (Image LabelKey, IntMap (Maybe LabelInfo))
31 label4 = label4With (==)
32
33 label4With :: (a -> a -> Bool) -> Image a -> (Image LabelKey, IntMap (Maybe LabelInfo))
34 label4With neighbours img = (img', inf) where
35 status = label4With' neighbours img (LabellingStatus (0, 0) 0 (newImage (witdthImage img, heightImage img) []) mempty mempty)
36 img' = fmap (applyEquivClass (_equivalences status)) (_mask status)
37 inf = foldrWithKey (mergeClass (_equivalences status)) mempty (_infoMap status)
38
39 applyEquivClass eqMap lbl = equivClass lbl eqMap
40
41 mergeClass eqMap label labelInfo = alter (updateMap labelInfo) (equivClass label eqMap)
42
43 updateMap Nothing Nothing = Nothing
44 updateMap (Just new) Nothing = Just (Just new)
45 updateMap Nothing (Just old) = Just old
46 updateMap (Just new) (Just (Just oldS)) = Just (Just (new <> oldS))
47 updateMap _ _ = Nothing
48
49 label4With' :: (a -> a -> Bool) -> Image a -> LabellingStatus -> LabellingStatus
50 label4With' neighbours img status = checkNext (nextCoords xy) (updateStatus mergeLabels) where
51 xy@(x, y) = _currentCoords status
52 pixel = pixelImage (x, y) img
53
54 mergeLabels = fmap getMaskLabel $ filter isNeighbour $ addPixelInfo <$> previousNeighbours xy
55
56 addPixelInfo (nx, ny) = (nx, ny, pixelImage (nx, ny) img)
57 isNeighbour (_, _, e) = neighbours pixel e
58 getMaskLabel (nx, ny, _) = pixelImage (nx, ny) (_mask status)
59
60 updateStatus [] = status { _nextKey = Extra.next (_nextKey status), _mask = setPixelImage (x, y) (_nextKey status) (_mask status), _infoMap = insert (_nextKey status) (addPixel (x, y) Nothing) (_infoMap status) }
61 updateStatus [l] = status { _mask = setPixelImage (x, y) l (_mask status), _infoMap = adjust (addPixel (x, y)) l (_infoMap status) }
62 updateStatus [l1, l2] = status { _mask = setPixelImage (x, y) (max l1 l2) (_mask status), _infoMap = adjust (addPixel (x, y)) (max l1 l2) (_infoMap status), _equivalences = equivInsert l1 l2 (_equivalences status) }
63 updateStatus _ = error "too many neighbours in HelVM.HelMA.Automata.Piet.Compiler.ImageProcessor.label4With'"
64 checkNext (Just xy') s = label4With' neighbours img (s { _currentCoords = xy' })
65 checkNext Nothing s = s
66
67 previousNeighbours (cx, cy) = filter validCoord [ (cx-1, cy), (cx, cy-1) ]
68 validCoord (nx, ny) = nx >= 0 && ny >= 0
69
70 nextCoords (cx, cy) = guardX (cx < witdthImage img - 1) cx cy
71 guardX True cx cy = Just (cx + 1, cy)
72 guardX False _ cy = guardY (cy < heightImage img - 1) cy
73 guardY True cy = Just (0, cy + 1)
74 guardY False _ = Nothing
75
76 type EquivalenceMap = IntMap LabelKey
77
78 equivClass :: LabelKey -> EquivalenceMap -> LabelKey
79 equivClass e = findWithDefault e e
80
81 equivInsert :: LabelKey -> LabelKey -> EquivalenceMap -> EquivalenceMap
82 equivInsert x y mp = guardInsert (x /= y) where
83 guardInsert True = fmap replaceClass $ insert x newClass $ insert y newClass mp
84 guardInsert False = mp
85
86 class1 = equivClass x mp
87 class2 = equivClass y mp
88 classes = x :| [y, class1, class2]
89 newClass = Extra.minimum1 classes
90
91 replaceClass eqClass = checkInClass (eqClass `elem` classes) eqClass
92 checkInClass True _ = newClass
93 checkInClass False eqc = eqc