Skip to content

Commit 2b2cbfe

Browse files
authored
add FloodFill.js
1 parent 229cca1 commit 2b2cbfe

File tree

1 file changed

+180
-0
lines changed

1 file changed

+180
-0
lines changed

Recursive/FloodFill.js

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
/**
2+
* Flood fill, also called seed fill, is an algorithm that determines and alters the area connected
3+
* to a given node in a multi-dimensional array with some matching attribute. It is used in the
4+
* "bucket" fill tool of paint programs to fill connected, similarly-colored areas with a different
5+
* color. (description adapted from https://en.wikipedia.org/wiki/Flood_fill) (see also:
6+
* https://www.techiedelight.com/flood-fill-algorithm/).
7+
*/
8+
9+
const neighbors = [[-1, -1], [-1, 0], [-1, 1], [0, -1], [0, 1], [1, -1], [1, 0], [1, 1]]
10+
11+
const black = [0, 0, 0]
12+
const green = [0, 255, 0]
13+
const violet = [255, 0, 255]
14+
const white = [255, 255, 255]
15+
const orange = [255, 128, 0] // eslint-disable-line
16+
17+
/*
18+
Doctests
19+
> testBreadthFirst([1, 1], green, orange, [1, 1]);
20+
orange
21+
> testBreadthFirst([1, 1], green, orange, [0, 1]);
22+
violet
23+
> testBreadthFirst([1, 1], green, orange, [6, 4]);
24+
white
25+
> testDepthFirst([1, 1], green, orange, [1, 1]);
26+
orange
27+
> testDepthFirst([1, 1], green, orange, [0, 1]);
28+
violet
29+
> testDepthFirst([1, 1], green, orange, [6, 4]);
30+
white
31+
*/
32+
33+
/**
34+
* Implements the flood fill algorithm through a breadth-first approach using a queue.
35+
*
36+
* @param rgbData The image to which the algorithm is applied.
37+
* @param location The start location on the image.
38+
* @param targetColor The old color to be replaced.
39+
* @param replacementColor The new color to replace the old one.
40+
*/
41+
function breadthFirstSearch (rgbData, location, targetColor, replacementColor) {
42+
if (location[0] < 0 ||
43+
location[0] >= rgbData.length ||
44+
location[1] < 0 ||
45+
location[1] >= rgbData[0].length) {
46+
throw new Error('location should point to a pixel within the rgbData')
47+
}
48+
49+
const queue = []
50+
queue.push(location)
51+
52+
while (queue.length > 0) {
53+
breadthFirstFill(rgbData, location, targetColor, replacementColor, queue)
54+
}
55+
}
56+
57+
/**
58+
* Implements the flood fill algorithm through a depth-first approach using recursion.
59+
*
60+
* @param rgbData The image to which the algorithm is applied.
61+
* @param location The start location on the image.
62+
* @param targetColor The old color to be replaced.
63+
* @param replacementColor The new color to replace the old one.
64+
*/
65+
function depthFirstSearch (rgbData, location, targetColor, replacementColor) {
66+
if (location[0] < 0 ||
67+
location[0] >= rgbData.length ||
68+
location[1] < 0 ||
69+
location[1] >= rgbData[0].length) {
70+
throw new Error('location should point to a pixel within the rgbData')
71+
}
72+
73+
depthFirstFill(rgbData, location, targetColor, replacementColor)
74+
}
75+
76+
/**
77+
* Utility-function to implement the breadth-first loop
78+
*
79+
* @param rgbData The image to which the algorithm is applied.
80+
* @param location The start location on the image.
81+
* @param targetColor The old color to be replaced.
82+
* @param replacementColor The new color to replace the old one.
83+
* @param queue The locations that still need to be visited.
84+
*/
85+
function breadthFirstFill (rgbData, location, targetColor, replacementColor, queue) {
86+
const currentLocation = queue[0]
87+
queue.shift()
88+
89+
if (rgbData[currentLocation[0]][currentLocation[1]] === targetColor) {
90+
rgbData[currentLocation[0]][currentLocation[1]] = replacementColor
91+
92+
for (let i = 0; i < neighbors.length; i++) {
93+
const x = currentLocation[0] + neighbors[i][0]
94+
const y = currentLocation[1] + neighbors[i][1]
95+
if (x >= 0 && x < rgbData.length && y >= 0 && y < rgbData[0].length) {
96+
queue.push([x, y])
97+
}
98+
}
99+
}
100+
}
101+
102+
/**
103+
* Utility-function to implement the depth-first loop
104+
*
105+
* @param rgbData The image to which the algorithm is applied.
106+
* @param location The start location on the image.
107+
* @param targetColor The old color to be replaced.
108+
* @param replacementColor The new color to replace the old one.
109+
*/
110+
function depthFirstFill (rgbData, location, targetColor, replacementColor) {
111+
if (rgbData[location[0]][location[1]] === targetColor) {
112+
rgbData[location[0]][location[1]] = replacementColor
113+
114+
for (let i = 0; i < neighbors.length; i++) {
115+
const x = location[0] + neighbors[i][0]
116+
const y = location[1] + neighbors[i][1]
117+
if (x >= 0 && x < rgbData.length && y >= 0 && y < rgbData[0].length) {
118+
depthFirstFill(rgbData, [x, y], targetColor, replacementColor)
119+
}
120+
}
121+
}
122+
}
123+
124+
/**
125+
* Generates the rgbData-matrix for the tests
126+
*
127+
* @return example rgbData-matrix
128+
*/
129+
function generateTestRgbData () {
130+
const layout = [
131+
[violet, violet, green, green, black, green, green],
132+
[violet, green, green, black, green, green, green],
133+
[green, green, green, black, green, green, green],
134+
[black, black, green, black, white, white, green],
135+
[violet, violet, black, violet, violet, white, white],
136+
[green, green, green, violet, violet, violet, violet],
137+
[violet, violet, violet, violet, violet, violet, violet]
138+
]
139+
140+
// transpose layout-matrix so the x-index comes before the y-index
141+
const transposed = []
142+
for (let x = 0; x < layout[0].length; x++) {
143+
transposed[x] = []
144+
for (let y = 0; y < layout.length; y++) {
145+
transposed[x][y] = layout[y][x]
146+
}
147+
}
148+
149+
return transposed
150+
}
151+
152+
/**
153+
* Utility-function to test the function "breadthFirstSearch"
154+
*
155+
* @param fillLocation The start location on the image where the flood fill is applied.
156+
* @param targetColor The old color to be replaced.
157+
* @param replacementColor The new color to replace the old one.
158+
* @param testLocation The location of the color to be checked.
159+
* @return The color at testLocation
160+
*/
161+
function testBreadthFirst (fillLocation, targetColor, replacementColor, testLocation) {// eslint-disable-line
162+
const rgbData = generateTestRgbData()
163+
breadthFirstSearch(rgbData, fillLocation, targetColor, replacementColor)
164+
return rgbData[testLocation[0]][testLocation[1]]
165+
}
166+
167+
/**
168+
* Utility-function to test the function "depthFirstSearch"
169+
*
170+
* @param fillLocation The start location on the image where the flood fill is applied.
171+
* @param targetColor The old color to be replaced.
172+
* @param replacementColor The new color to replace the old one.
173+
* @param testLocation The location of the color to be checked.
174+
* @return The color at testLocation
175+
*/
176+
function testDepthFirst (fillLocation, targetColor, replacementColor, testLocation) {// eslint-disable-line
177+
const rgbData = generateTestRgbData()
178+
depthFirstSearch(rgbData, fillLocation, targetColor, replacementColor)
179+
return rgbData[testLocation[0]][testLocation[1]]
180+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy