forked from TheAlgorithms/JavaScript
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathFloodFill.js
180 lines (162 loc) · 6.36 KB
/
FloodFill.js
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
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
/**
* Flood fill, also called seed fill, is an algorithm that determines and alters the area connected
* to a given node in a multi-dimensional array with some matching attribute. It is used in the
* "bucket" fill tool of paint programs to fill connected, similarly-colored areas with a different
* color. (description adapted from https://en.wikipedia.org/wiki/Flood_fill) (see also:
* https://www.techiedelight.com/flood-fill-algorithm/).
*/
const neighbors = [[-1, -1], [-1, 0], [-1, 1], [0, -1], [0, 1], [1, -1], [1, 0], [1, 1]]
const black = [0, 0, 0]
const green = [0, 255, 0]
const violet = [255, 0, 255]
const white = [255, 255, 255]
const orange = [255, 128, 0] // eslint-disable-line
/*
Doctests
> testBreadthFirst([1, 1], green, orange, [1, 1]);
orange
> testBreadthFirst([1, 1], green, orange, [0, 1]);
violet
> testBreadthFirst([1, 1], green, orange, [6, 4]);
white
> testDepthFirst([1, 1], green, orange, [1, 1]);
orange
> testDepthFirst([1, 1], green, orange, [0, 1]);
violet
> testDepthFirst([1, 1], green, orange, [6, 4]);
white
*/
/**
* Implements the flood fill algorithm through a breadth-first approach using a queue.
*
* @param rgbData The image to which the algorithm is applied.
* @param location The start location on the image.
* @param targetColor The old color to be replaced.
* @param replacementColor The new color to replace the old one.
*/
function breadthFirstSearch (rgbData, location, targetColor, replacementColor) {
if (location[0] < 0 ||
location[0] >= rgbData.length ||
location[1] < 0 ||
location[1] >= rgbData[0].length) {
throw new Error('location should point to a pixel within the rgbData')
}
const queue = []
queue.push(location)
while (queue.length > 0) {
breadthFirstFill(rgbData, location, targetColor, replacementColor, queue)
}
}
/**
* Implements the flood fill algorithm through a depth-first approach using recursion.
*
* @param rgbData The image to which the algorithm is applied.
* @param location The start location on the image.
* @param targetColor The old color to be replaced.
* @param replacementColor The new color to replace the old one.
*/
function depthFirstSearch (rgbData, location, targetColor, replacementColor) {
if (location[0] < 0 ||
location[0] >= rgbData.length ||
location[1] < 0 ||
location[1] >= rgbData[0].length) {
throw new Error('location should point to a pixel within the rgbData')
}
depthFirstFill(rgbData, location, targetColor, replacementColor)
}
/**
* Utility-function to implement the breadth-first loop
*
* @param rgbData The image to which the algorithm is applied.
* @param location The start location on the image.
* @param targetColor The old color to be replaced.
* @param replacementColor The new color to replace the old one.
* @param queue The locations that still need to be visited.
*/
function breadthFirstFill (rgbData, location, targetColor, replacementColor, queue) {
const currentLocation = queue[0]
queue.shift()
if (rgbData[currentLocation[0]][currentLocation[1]] === targetColor) {
rgbData[currentLocation[0]][currentLocation[1]] = replacementColor
for (let i = 0; i < neighbors.length; i++) {
const x = currentLocation[0] + neighbors[i][0]
const y = currentLocation[1] + neighbors[i][1]
if (x >= 0 && x < rgbData.length && y >= 0 && y < rgbData[0].length) {
queue.push([x, y])
}
}
}
}
/**
* Utility-function to implement the depth-first loop
*
* @param rgbData The image to which the algorithm is applied.
* @param location The start location on the image.
* @param targetColor The old color to be replaced.
* @param replacementColor The new color to replace the old one.
*/
function depthFirstFill (rgbData, location, targetColor, replacementColor) {
if (rgbData[location[0]][location[1]] === targetColor) {
rgbData[location[0]][location[1]] = replacementColor
for (let i = 0; i < neighbors.length; i++) {
const x = location[0] + neighbors[i][0]
const y = location[1] + neighbors[i][1]
if (x >= 0 && x < rgbData.length && y >= 0 && y < rgbData[0].length) {
depthFirstFill(rgbData, [x, y], targetColor, replacementColor)
}
}
}
}
/**
* Generates the rgbData-matrix for the tests
*
* @return example rgbData-matrix
*/
function generateTestRgbData () {
const layout = [
[violet, violet, green, green, black, green, green],
[violet, green, green, black, green, green, green],
[green, green, green, black, green, green, green],
[black, black, green, black, white, white, green],
[violet, violet, black, violet, violet, white, white],
[green, green, green, violet, violet, violet, violet],
[violet, violet, violet, violet, violet, violet, violet]
]
// transpose layout-matrix so the x-index comes before the y-index
const transposed = []
for (let x = 0; x < layout[0].length; x++) {
transposed[x] = []
for (let y = 0; y < layout.length; y++) {
transposed[x][y] = layout[y][x]
}
}
return transposed
}
/**
* Utility-function to test the function "breadthFirstSearch"
*
* @param fillLocation The start location on the image where the flood fill is applied.
* @param targetColor The old color to be replaced.
* @param replacementColor The new color to replace the old one.
* @param testLocation The location of the color to be checked.
* @return The color at testLocation
*/
function testBreadthFirst (fillLocation, targetColor, replacementColor, testLocation) {// eslint-disable-line
const rgbData = generateTestRgbData()
breadthFirstSearch(rgbData, fillLocation, targetColor, replacementColor)
return rgbData[testLocation[0]][testLocation[1]]
}
/**
* Utility-function to test the function "depthFirstSearch"
*
* @param fillLocation The start location on the image where the flood fill is applied.
* @param targetColor The old color to be replaced.
* @param replacementColor The new color to replace the old one.
* @param testLocation The location of the color to be checked.
* @return The color at testLocation
*/
function testDepthFirst (fillLocation, targetColor, replacementColor, testLocation) {// eslint-disable-line
const rgbData = generateTestRgbData()
depthFirstSearch(rgbData, fillLocation, targetColor, replacementColor)
return rgbData[testLocation[0]][testLocation[1]]
}