This repository was archived by the owner on Jul 24, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
This repository was archived by the owner on Jul 24, 2023. It is now read-only.
Paint fill function for an image #3
Copy link
Copy link
Open
Description
This is the paint-fill function. @balacij
algorithm-design-with-haskell/src/Recursion.hs
Lines 372 to 499 in b3f39e5
{- | |
8.10 | |
Implement the "paint fill" function that one might see on many image | |
editing programs. That is, given a screen (represented by a | |
two-dimensional array of colors), a point, and a new color, fill in | |
the surrounding area until the color changes from the original color. | |
test case: fillUpColor image (0,0) Blue | |
original image: | |
[ | |
[Red,Red,Red], | |
[Red,Yellow,Red], | |
[Yellow,Yellow,Blue] | |
] | |
expect image: | |
[ | |
[Blue,Blue,Blue], | |
[Blue,Yellow,Blue], | |
[Yellow,Yellow,Blue] | |
] | |
-} | |
data Color = Red | Yellow | Blue deriving Show | |
instance Eq Color where | |
Red == Red = True | |
Red == Yellow = False | |
Red == Blue = False | |
Yellow == Yellow = True | |
Yellow == Blue = False | |
Yellow == Red = False | |
Blue == Blue = True | |
Blue == Yellow = False | |
Blue == Red = False | |
data Direction = Up | Down | PLeft | PRight | |
type Image = V.Vector (V.Vector Color) | |
-- test case | |
image :: Image | |
image = V.fromList | |
[ | |
V.fromList [Red, Red, Red], | |
V.fromList [Red, Yellow, Red], | |
V.fromList [Yellow, Yellow, Blue] | |
] | |
-- fill up a color | |
fillUpColor :: Image -> (Int, Int) -> Color -> Image | |
fillUpColor img (i,j) c = foldl (\acc x -> paint acc x c ) img pList | |
where pList = findArea img (i,j) | |
-- Paint a color in one location | |
paint :: Image -> (Int, Int) -> Color -> Image | |
paint vs (i,j) c = | |
fstHVects V.++ V.fromList[newPaintRow] V.++ V.drop 1 secHVects | |
where | |
(fstHVects, secHVects) = V.splitAt i vs | |
(fstHPaintRow, secHPaintRow) = V.splitAt j (vs V.! i) | |
newPaintRow = | |
fstHPaintRow V.++ V.fromList[c] V.++ V.drop 1 secHPaintRow | |
-- Find all locations which need to paint | |
findArea :: Image -> (Int, Int) -> [(Int, Int)] | |
findArea img (i,j) = uniq ( | |
(i,j): | |
findAreaOnDir img (i,j) boundC Up ++ | |
findAreaOnDir img (i,j) boundC Down ++ | |
findAreaOnDir img (i,j) boundC PLeft ++ | |
findAreaOnDir img (i,j) boundC PRight) [] | |
where boundC = img V.! i V.! j | |
-- remove duplicates | |
uniq :: [(Int, Int)] -> [(Int, Int)]-> [(Int, Int)] | |
uniq [] buf = buf | |
uniq (x:xs) buf | |
| x `elem` buf = uniq xs buf | |
| otherwise = uniq xs (x:buf) | |
-- find potential position by direction | |
findAreaOnDir :: Image -> (Int, Int) -> Color -> Direction -> [(Int, Int)] | |
findAreaOnDir img (i,j) c Up | |
| isInBoundAndSameColor img (i,j-1) c = | |
(i,j-1): findAreaOnDir img (i,j-1) c PLeft | |
| isInBoundAndSameColor img (i-1,j) c = | |
(i-1,j): findAreaOnDir img (i-1,j) c Up | |
| isInBoundAndSameColor img (i,j+1) c = | |
(i,j+1): findAreaOnDir img (i,j+1) c PRight | |
| otherwise = [] | |
findAreaOnDir img (i,j) c Down | |
| isInBoundAndSameColor img (i,j-1) c = | |
(i,j-1): findAreaOnDir img (i,j-1) c PLeft | |
| isInBoundAndSameColor img (i+1,j) c = | |
(i+1,j): findAreaOnDir img (i+1,j) c Down | |
| isInBoundAndSameColor img (i,j+1) c = | |
(i,j+1): findAreaOnDir img (i,j+1) c PRight | |
| otherwise = [] | |
findAreaOnDir img (i,j) c PLeft | |
| isInBoundAndSameColor img (i-1,j) c = | |
(i-1,j): findAreaOnDir img (i-1,j) c Up | |
| isInBoundAndSameColor img (i,j-1) c = | |
(i,j-1): findAreaOnDir img (i,j-1) c PLeft | |
| isInBoundAndSameColor img (i+1,j) c = | |
(i+1,j): findAreaOnDir img (i+1,j) c Down | |
| otherwise = [] | |
findAreaOnDir img (i,j) c PRight | |
| isInBoundAndSameColor img (i-1,j) c = | |
(i-1,j): findAreaOnDir img (i-1,j) c Up | |
| isInBoundAndSameColor img (i,j+1) c = | |
(i,j+1): findAreaOnDir img (i,j+1) c PRight | |
| isInBoundAndSameColor img (i+1,j) c = | |
(i+1,j): findAreaOnDir img (i+1,j) c Down | |
| otherwise = [] | |
-- condition determine potential fill up position | |
isInBoundAndSameColor :: Image -> (Int, Int) -> Color -> Bool | |
isInBoundAndSameColor img (i,j) c = isInBound img (i,j) && selectC == c | |
where selectC = img V.! i V.! j | |
-- check if position if in bound | |
isInBound :: Image -> (Int, Int) -> Bool | |
isInBound img (i,j) | |
| (0 <= i && i < xBound) && (0 <= j && j < yBound) = True | |
| otherwise = False | |
where xBound = length img | |
yBound = length $ img V.! 0 |
Let me know what you think. I think there should be a way to simplify the findAreaOnDir
method. It looks very cumbersome.
Metadata
Metadata
Assignees
Labels
No labels