From 17ec268d8f69a7aebe09ec60a0b66870daf72c31 Mon Sep 17 00:00:00 2001 From: Jonas Rinke Date: Wed, 2 Sep 2020 11:43:18 +0200 Subject: [PATCH 1/2] Added flood fill in JS --- .../flood_fill/code/javascript/flood_fill.js | 103 ++++++++++++++++++ contents/flood_fill/flood_fill.md | 12 ++ 2 files changed, 115 insertions(+) create mode 100644 contents/flood_fill/code/javascript/flood_fill.js diff --git a/contents/flood_fill/code/javascript/flood_fill.js b/contents/flood_fill/code/javascript/flood_fill.js new file mode 100644 index 000000000..9cccde542 --- /dev/null +++ b/contents/flood_fill/code/javascript/flood_fill.js @@ -0,0 +1,103 @@ +// This is a p5.js sketch. +// https://p5js.org + +function isInBounds(x, y) { + return (x >= 0) && (x < width) && (y >= 0) && (y < height) +} + +function isColor(x, y, color) { + const index = (x + y * width) * 4 + return pixels[index] == red(color) && + pixels[index + 1] == green(color) && + pixels[index + 2] == blue(color) && + pixels[index + 3] == alpha(color) +} + +function recolorPixel(x, y, oldColor, newColor) { + if (isInBounds(x, y) && isColor(x, y, oldColor)) { + const index = (x + y * width) * 4 + pixels[index] = red(newColor) + pixels[index + 1] = green(newColor) + pixels[index + 2] = blue(newColor) + pixels[index + 3] = alpha(newColor) + } +} + +function findNeighbors(x, y, oldColor) { + const allNeighbors = [ + [x, y - 1], // North + [x + 1, y], // East + [x, y + 1], // South + [x - 1, y] // West + ] + + return allNeighbors + .filter(loc => isInBounds(...loc)) + .filter(loc => isColor(...loc, oldColor)) +} + +function stackFill(x, y, oldColor, newColor) { + const stack = [ + [x, y] + ] + + while (stack.length > 0) { + const currentLoc = stack.pop() + recolorPixel(...currentLoc, oldColor, newColor) + + for(const n of findNeighbors(...currentLoc, oldColor)) + stack.push(n) + } +} + +function queueFill(x, y, oldColor, newColor) { + const queue = [ + [x, y] + ] + + while (queue.length > 0) { + const currentLoc = queue.shift() + recolorPixel(...currentLoc, oldColor, newColor) + + for (const n of findNeighbors(...currentLoc, oldColor)) { + // Color neighbor pixel before enqueuing to prevent + // it from being colored multiple times + recolorPixel(...n, oldColor, newColor) + queue.push(n) + } + } +} + +function recursiveFill(x, y, oldColor, newColor) { + recolorPixel(x, y, oldColor, newColor) + + for(const n of findNeighbors(x, y, oldColor)) + recursiveFill(...n, oldColor, newColor) +} + +function setup() { + createCanvas(600, 200); + noLoop() +} + +function draw() { + const bgColor = color(0, 0, 0, 0) + background(bgColor); + + noFill() + stroke(0) + strokeWeight(3) + triangle(80, 80, 160, 100, 100, 140) + triangle(340, 30, 380, 170, 230, 120) + triangle(480, 40, 580, 140, 430, 160) + + loadPixels() + + // recursiveFill tends to blow up the stack for large + // areas so it gets a smaller triangle + recursiveFill(100, 100, bgColor, color(30, 245, 250)) + stackFill(300, 100, bgColor, color(130, 50, 180)) + queueFill(500, 100, bgColor, color(50, 255, 225)) + + updatePixels() +} diff --git a/contents/flood_fill/flood_fill.md b/contents/flood_fill/flood_fill.md index 836f86897..ae21794db 100644 --- a/contents/flood_fill/flood_fill.md +++ b/contents/flood_fill/flood_fill.md @@ -90,6 +90,8 @@ In code, this might look like this: [import:37-55, lang:"julia"](code/julia/flood_fill.jl) {% sample lang="c" %} [import:34-52, lang:"c"](code/c/flood_fill.c) +{% sample lang="js" %} +[import:26-37, lang:"javascript"](code/javascript/flood_fill.js) {% endmethod %} @@ -106,6 +108,8 @@ In code, it might look like this: [import:106-118, lang:"julia"](code/julia/flood_fill.jl) {% sample lang="c" %} [import:180-195, lang:"c"](code/c/flood_fill.c) +{% sample lang="js" %} +[import:71-76, lang:"javascript"](code/javascript/flood_fill.js) {% endmethod %} All code snippets for this chapter rely on an exterior `color` function, defined as @@ -115,6 +119,8 @@ All code snippets for this chapter rely on an exterior `color` function, defined [import:23-35, lang:"julia"](code/julia/flood_fill.jl) {% sample lang="c" %} [import:28-32, lang:"c"](code/c/flood_fill.c) +{% sample lang="js" %} +[import:16-24, lang:"javascript"](code/javascript/flood_fill.js) {% endmethod %} The above code continues recursing through available neighbors as long as neighbors exist, and this should work so long as we are adding the correct set of neighbors. @@ -126,6 +132,8 @@ Additionally, it is possible to do the same type of traversal by managing a stac [import:57-77, lang:"julia"](code/julia/flood_fill.jl) {% sample lang="c" %} [import:85-108, lang:"c"](code/c/flood_fill.c) +{% sample lang="js" %} +[import:39-51, lang:"javascript"](code/javascript/flood_fill.js) {% endmethod %} This is ultimately the same method of traversal as before; however, because we are managing our own data structure, there are a few distinct differences: @@ -165,6 +173,8 @@ The code would look something like this: [import:80-104, lang:"julia"](code/julia/flood_fill.jl) {% sample lang="c" %} [import:155-178, lang:"c"](code/c/flood_fill.c) +{% sample lang="js" %} +[import:53-69, lang:"javascript"](code/javascript/flood_fill.js) {% endmethod %} Now, there is a small trick in this code that must be considered to make sure it runs optimally. @@ -243,6 +253,8 @@ After, we will fill in the left-hand side of the array to be all ones by choosin [import, lang:"julia"](code/julia/flood_fill.jl) {% sample lang="c" %} [import, lang:"c"](code/c/flood_fill.c) +{% sample lang="js" %} +[import, lang:"javascript"](code/javascript/flood_fill.js) {% endmethod %} From e50682708db9cfb3f597a9b0e4fb8871dce5b18a Mon Sep 17 00:00:00 2001 From: Jonas Rinke Date: Wed, 14 Oct 2020 00:47:05 +0200 Subject: [PATCH 2/2] Removed redundant checks mentioned in #765 --- .../flood_fill/code/javascript/flood_fill.js | 18 ++++++++---------- contents/flood_fill/flood_fill.md | 19 ++++--------------- 2 files changed, 12 insertions(+), 25 deletions(-) diff --git a/contents/flood_fill/code/javascript/flood_fill.js b/contents/flood_fill/code/javascript/flood_fill.js index 9cccde542..8180869ab 100644 --- a/contents/flood_fill/code/javascript/flood_fill.js +++ b/contents/flood_fill/code/javascript/flood_fill.js @@ -14,13 +14,11 @@ function isColor(x, y, color) { } function recolorPixel(x, y, oldColor, newColor) { - if (isInBounds(x, y) && isColor(x, y, oldColor)) { - const index = (x + y * width) * 4 - pixels[index] = red(newColor) - pixels[index + 1] = green(newColor) - pixels[index + 2] = blue(newColor) - pixels[index + 3] = alpha(newColor) - } + const index = (x + y * width) * 4 + pixels[index] = red(newColor) + pixels[index + 1] = green(newColor) + pixels[index + 2] = blue(newColor) + pixels[index + 3] = alpha(newColor) } function findNeighbors(x, y, oldColor) { @@ -45,7 +43,7 @@ function stackFill(x, y, oldColor, newColor) { const currentLoc = stack.pop() recolorPixel(...currentLoc, oldColor, newColor) - for(const n of findNeighbors(...currentLoc, oldColor)) + for (const n of findNeighbors(...currentLoc, oldColor)) stack.push(n) } } @@ -71,7 +69,7 @@ function queueFill(x, y, oldColor, newColor) { function recursiveFill(x, y, oldColor, newColor) { recolorPixel(x, y, oldColor, newColor) - for(const n of findNeighbors(x, y, oldColor)) + for (const n of findNeighbors(x, y, oldColor)) recursiveFill(...n, oldColor, newColor) } @@ -100,4 +98,4 @@ function draw() { queueFill(500, 100, bgColor, color(50, 255, 225)) updatePixels() -} +} \ No newline at end of file diff --git a/contents/flood_fill/flood_fill.md b/contents/flood_fill/flood_fill.md index ae21794db..07a0c727e 100644 --- a/contents/flood_fill/flood_fill.md +++ b/contents/flood_fill/flood_fill.md @@ -91,7 +91,7 @@ In code, this might look like this: {% sample lang="c" %} [import:34-52, lang:"c"](code/c/flood_fill.c) {% sample lang="js" %} -[import:26-37, lang:"javascript"](code/javascript/flood_fill.js) +[import:24-35, lang:"javascript"](code/javascript/flood_fill.js) {% endmethod %} @@ -109,18 +109,7 @@ In code, it might look like this: {% sample lang="c" %} [import:180-195, lang:"c"](code/c/flood_fill.c) {% sample lang="js" %} -[import:71-76, lang:"javascript"](code/javascript/flood_fill.js) -{% endmethod %} - -All code snippets for this chapter rely on an exterior `color` function, defined as - -{% method %} -{% sample lang="jl" %} -[import:23-35, lang:"julia"](code/julia/flood_fill.jl) -{% sample lang="c" %} -[import:28-32, lang:"c"](code/c/flood_fill.c) -{% sample lang="js" %} -[import:16-24, lang:"javascript"](code/javascript/flood_fill.js) +[import:69-74, lang:"javascript"](code/javascript/flood_fill.js) {% endmethod %} The above code continues recursing through available neighbors as long as neighbors exist, and this should work so long as we are adding the correct set of neighbors. @@ -133,7 +122,7 @@ Additionally, it is possible to do the same type of traversal by managing a stac {% sample lang="c" %} [import:85-108, lang:"c"](code/c/flood_fill.c) {% sample lang="js" %} -[import:39-51, lang:"javascript"](code/javascript/flood_fill.js) +[import:37-49, lang:"javascript"](code/javascript/flood_fill.js) {% endmethod %} This is ultimately the same method of traversal as before; however, because we are managing our own data structure, there are a few distinct differences: @@ -174,7 +163,7 @@ The code would look something like this: {% sample lang="c" %} [import:155-178, lang:"c"](code/c/flood_fill.c) {% sample lang="js" %} -[import:53-69, lang:"javascript"](code/javascript/flood_fill.js) +[import:51-67, lang:"javascript"](code/javascript/flood_fill.js) {% endmethod %} Now, there is a small trick in this code that must be considered to make sure it runs optimally.