Skip to content
This repository has been archived by the owner on Jul 24, 2023. It is now read-only.

Paint fill function for an image #3

Open
cd155 opened this issue Feb 4, 2023 · 5 comments
Open

Paint fill function for an image #3

cd155 opened this issue Feb 4, 2023 · 5 comments

Comments

@cd155
Copy link
Owner

cd155 commented Feb 4, 2023

This is the paint-fill function. @balacij

{-
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.

@cd155
Copy link
Owner Author

cd155 commented Feb 21, 2023

@balacij
Copy link

balacij commented Mar 3, 2023

Looks much better! 😄

Are you able to do it row-wise instead of point-wise out of curiosity?

@cd155
Copy link
Owner Author

cd155 commented Mar 3, 2023

Looks much better! smile

Are you able to do it row-wise instead of point-wise out of curiosity?

@balacij I actually not sure how to this question row-wise. The current way is to find all the coordinates of possible points that need to be painted. Once find all of them, then paint them accordingly.

@cd155
Copy link
Owner Author

cd155 commented Mar 3, 2023

The ideology of row-wise seems very interesting. Thanks for pointing it out. It's doable, and I will keep you in the post.

@balacij
Copy link

balacij commented Mar 4, 2023

It was just a thought on how an algorithm could be done if you know the ranges of the "insides" of the shape by length along the horizontal axis. I hope it works 😄

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants