Color all adjacent same tiles iteratively - javascript

I try to create a paint bucket tool. I need to find all adjacents points of the point I clicked, and change their color if they have the same color than the original. The color need to propagate on all points with same color. The propagation can only be done on 4 directions (no diagonals).
I can do this easily recursively, but sadly I get an error when the map is too big :
Uncaught RangeError: Maximum call stack size exceeded
This is a basic example to reproduce the problem, and I would like to transform it to iterative way:
// Fill the map
var map = [];
for (var x = 0; x < 500; x++){
var row = [];
for (var y = 0; y < 500; y++){
row.push(1);
}
map.push(row);
}
var paintTile = function(x, y){
// If X or Y is out of map range
if (x < 0 || x >= 500 || y < 0 || y >= 500){
return
}
// If this tile is already painted in new color
if (map[x][y] == 0){
return;
}
// Draw tile with new color
map[x][y] = 0;
// Paint all adjacent tiles
paintTile(x - 1, y);
paintTile(x + 1, y);
paintTile(x, y - 1);
paintTile(x, y + 1);
};
paintTile(0, 0);
In this example, all the map is populated of "1" (let's say it's white color), and I transform them to "0" (black color), but I get this stack size error.
Regards

Do you mean Flood fill algorithm?
From http://en.wikipedia.org/wiki/Flood_fill:
Flood-fill (node, target-color, replacement-color):
1. Set Q to the empty queue.
2. If the color of node is not equal to target-color, return.
3. Add node to Q.
4. For each element N of Q:
5. If the color of N is equal to target-color:
6. Set w and e equal to N.
7. Move w to the west until the color of the node to the west of w no longer matches target-color.
8. Move e to the east until the color of the node to the east of e no longer matches target-color.
9. For each node n between w and e:
10. Set the color of n to replacement-color.
11. If the color of the node to the north of n is target-color, add that node to Q.
12. If the color of the node to the south of n is target-color, add that node to Q.
13. Continue looping until Q is exhausted.
14. Return.

Keep a list of tiles you need to process and tiles you have processed. Put your first tile in the ToBeProcessed list and then loop repeatedly until your to be processed list is empty.
On each loop firstly check if you want to recolour this tile or not. If you don't then remove it from the ToBeProcessed and go to the next cycle of the loop. If it does then do the normal processing (ie change the colour). Then add the adjacent tiles to the ToBeProcessed list if they aren't on the already processed list (or already in the ToBeProcessed list).
Then at the end of your loop iteration remove the current item from the ToBeProcessed list.

here is a video on the topic: https://www.youtube.com/watch?v=LvacRISl99Y
Instead of using complex logic to track previously verified neighbor spaces, I have a 2D array that records all the verified spaces. Reading from the verified array is 2-3 instructions: IF pixel[23,23] is verified, THEN fill it and check it's neighbors.

Related

Javascript: How many arrays of 7 elements can i get out of an array of 49 elements

I am given an array with 49
My task is to select unique groups of 7 elements and also
find out the number of possible outputs.
Eg. [A,a,B,b,C,c,D,d,E,e,F,f,G,g,H,h,I,i,J,j,K,k,L,l,M,m,N,n,O,o,P,p,Q,q,R,r,S,s,T,t,U,u,V,v,W,w,X,x,Y]
Then outputs:
[ [A,a,B,b,C,c,D], [a,B,b,C,c,D,d], [B,b,C,c,D,d,E], [b,C,c,D,d,E,e],
. . . [A,C,c,D,d,E,e], [A,B,b,c,D,d,E],
.
.
.
]
How do I get this outputs?
Below is what I have tried based on the answer i got from stackoverflow:
let jar = ["A","a","B","b","C","c","D","d","E","e","F","f","G","g","H","h","I","i","J","j","K","k","L","l","M","m","N","n","O","o","P","p","Q","q","R","r","S","s","T","t","U","u","V","v","W","w","X","x","Y"];
const size = 7;
let result11 = [];
for(let i = 0; i <= (jar.length - size); i++){
result11.push(jar.slice(i, size+i));
}
console.log(result11)
Each of the output should be unique such that there should not be any repetition. Eg. outputs like aaaaaaa, aaxxxYY, AAAAAAA, AABbcDe are not valid but outputs like avWwXxY,bvWwXxY,cvWwXxY alongside others etc
Below you will find a working snippet that will generate all unique draws from a given array. Starting point is the initial draw vector v. This vector must contain exactly the number of array elements you want to have in each draw and the numbers must be in ascending order. Based on this initial vector the function nextDraw() will pick the next possible combination from the available number pool. The number pool is defined by 0 as the lower limit and n-1 as the upper limit.
My snippet is currently limited to 1000 draws as most of you would probably loose interest if you had to wait for all 85900584 possible combinations to be calculated and printed here.
function nextDraw(v,n){
// generate a new vector w by copying v
// set k to point to the last element of w:
let w=v.slice(0),l=w.length,k=l-1;
while (true){ // unconditional loop
// Is the k-th element of w lower than n-l+k?
if (w[k]<n-l+k) {
// increment w[k]
w[k]++;
// and, in case k<l-1:
// initialise all following elements of w with an increasing sequence:
for (let j=k+1;j<l;j++) {
w[j]=w[j-1]+1;
}
// return a valid draw vector!
return w;
}
else {
// as long as there is still a smaller k index available: decrement k
// and continue with the while loop
if(k) k--
// else: we have reached the end of the series!
else return false;
}
}
}
function allDraws(v,n){
const res=[];
res.push(v);
while (res.length<1000 && (v=nextDraw(v,n))) res.push(v)
return res;
}
// Show first 1000 and last 121 draws:
[[0,1,2,3,4,5,6],[38,43,44,45,46,47,48]].forEach(v=>{
let res=allDraws(v,49);
console.log(res.length,res.map(r=>r.join(",")));
});
Admittedly, my snippet works with index numbers and not with an array of arbitrary elements, but you can easily apply the calculated index vectors to retrieve the actual values from your source array of length n.

PIXI Logic is Skewed from the Actual Visuals

I am pretty new to JavaScript and PIXI. I am making a little game that requires the player to navigate and grab a key (move onto the same space as the key). This takes them to the next level. My problem is that whenever a new level is generated, it somehow uses the x and y coordinates for the key of the previous level to skew the logic. Because of this, the first level works fine, but every level afterwards is messed up.
The levels get skewed up and to the left by the respective values of the previous key. I have a 2d array in the background which holds values of where and where not the player can (holds values for things like walls, turrets, and the key). I randomly generate the key coordinates and for example set grid[9][12] = 3; for the key space. The next level visually looks perfect, with the walls and key generating, and the player being in the top left. However because of the skew, the top left of the visuals is really at [max_X - 9][max_Y - 12] and the player can go off screen to invisibly access the rest of the maze. So I can then phase through walls because the logic has the walls in a different place than the visuals, and the actual key space usually ends up off screen.
Here is my code that generates a new goal (key) object
function createGoal(scene) {
while (true) {
let x = getRandomInt(APP_WIDTH);
let y = getRandomInt(APP_HEIGHT);
//if space is not already occupied, populate
if (grid[x][y] == 0) {
console.log(x);
console.log(y);
goal = null;
goal = new Goal(TILE_WIDTH, x, y);
scene.addChild(goal);
grid[x][y] = 3;
break;
}
}
}
And here is the grid initialization:
const grid = new Array(APP_WIDTH);
for (let i = 0; i < APP_WIDTH; i++) {
grid[i] = new Array(APP_HEIGHT);
for (let j = 0; j < APP_HEIGHT; j++) {
grid[i][j] = 0;
}
}
Any help would be greatly appreciated.

How can I avoid overlapping when creating sprites? JS - PHASER3

I'm new in this and I'm making a small game in JS, the problem that I have now is when I create enemies it sometimes overlaps, creating this:
The way that use to create them is simple,
resetShip(enemy_spaceship) {
enemy_spaceship.y = 0;
enemy_spaceship.x = Phaser.Math.Between(10,globalThis.config.width);
}
In X each sprite will have a random number from 10 to the width of the screen (canvas), the problem is that if a sprite has 440 in X and another one has 450 in X, those 10px aren't enough to separate them, some people told me to create a grid, but like I said I'm new and searching about grid can't find any example that I can use to this, thanks if you can help me :)
One option is for each enemy ship to be allocated a specific region in which it may start. If you have 2 ships, that means the first ship can be anywhere in the first half of the X axis, and the second ship can be anywhere in the second half of the X axis.
To do this, you should update your resetShip function to also take in a minX and maxX, and use that when defining it's location:
resetShip (enemy_spaceship, minX, maxX) {
enemy_spaceship.y = 0;
enemy_spaceship.x = Phaser.Math.Between(minX, maxX);
}
Then, you need to find a way to rest the group of ships, providing valid regions for each ship. Something like this:
resetEnemies(ships) {
//Each ship may be in a region that is 1/Nth of the width
let regionWidth = globalThis.config.width / ships.length
//We need to know the shipWidth so we don't let ships get too
//close to the left edge.
let shipWidth = 64
ships.forEach((ship, i) => {
//Assuming you just want padding on the left so it is no closer than 10px,
//this will define the minX for the Nth ship
const minX = Math.min(10, i*regionWidth)
//The maxX should not let a ship overlap the next region. So, we subtract the shipWidth
//to ensure that, at worst, it is right next to the next ship
const maxX = (i+1)*regionWidth-shipWidth
//Use the updated restShip to put it in a valid location for it's region
resetShip(ship, minX, maxX)
})
}

Tooltips for data in javascript using p5.js

I am trying to make tooltips for a data visualization I made using p5.js but I am completely lost. Nothing I tried works. This is my code as is.
var table;
var i;
var j;
var cellValue;
var label;
var test;
function preload() {
matrix = loadTable("dataLayer2matrix.csv","csv")
labels = loadTable("dataLayer2labels.csv","csv")
test = matrix
}
function setup() {
createCanvas(1500,1500)
noStroke()
fill(0,0,255,10)
angleMode(DEGREES)
background(255,255,255)
matrixStartX = 200
matrixStartY = 250
var matrixRows = matrix.getRows()
var matrixSize = matrixRows.length
// Experiment with grid
fill(75, 75, 75, 50)
for (r = 0; r <= matrixSize; r++) {
rect(matrixStartX , matrixStartY + r * 20 - 1 , 20 * matrixSize, 1)
rect(matrixStartX + r * 20 - 1 , matrixStartY, 1, 20 * matrixSize)
}
// Draw matrix
for (var mr = 0; mr < matrixSize; mr++) {
for (var mc = 0; mc < matrixSize; mc++) {
cellValue = matrixRows[mr].getNum(mc)
fill(49,130,189,cellValue*10)
rect(mc * 20 + matrixStartX, mr * 20 + matrixStartY, 19 ,19)
}
}
// Labels - horizontal
fill(75, 75, 75, 255)
labelsRow = labels.getRows()
for (mc = 0; mc < matrixSize; mc++) {
label = labelsRow[0].getString(mc)
text(label, 10, mc*20+matrixStartY + 15)
}
// Labels - vertical
push()
translate(matrixStartX + 15, matrixStartY - 15)
rotate(-90)
for (mc = 0; mc < matrixSize; mc++) {
label = labelsRow[0].getString(mc)
text(label, 0, mc*20)
}
pop()
//Tooltip when clicked
}
/* if(mouseIsPressed){
fill(50);
text(cellValue, 10,10,70,80);
}*/
}
}
It makes this image:
I want it so that when I go over a square I get the data in it. I really can't seem to do it. Thanks.
I think the advice telling you to use bootstrap is missing the fact that you're using p5.js. Bootstrap is more for dealing with html components, not internal Processing sketches.
Instead, you probably want to do this with p5.js code. The best thing you can do is break your problem down into smaller steps:
Step 1: Can you draw a single rectangle?
Instead of trying to add this new functionality to your existing sketch, it might be easier if you start with a simpler example sketch with just a single rectangle.
Step 2: Can you detect when the mouse is inside that rectangle?
If you know where you're drawing the rectangle, you know its coordinates. You also know the coordinates of the mouse from the mouseX and mouseY variables. So to detect whether the mouse is inside the rectangle, you simply have to use if statements that compare the coordinates of the mouse to the coordinates of the rectangle. There are a ton of resources on google for this, and it might help if you draw some examples out on a piece of paper.
Also, don't worry about the tooltip just yet. Just do something simple like change the color of the rectangle when the mouse is inside it.
Step 3: Can you display the information box?
Again, do this in its own sketch first. Maybe create a function that takes a position and the information you want to display as parameters and displays it in a rectangle. Don't worry about making it a tooltip yet. Just get it displaying. Use hard-coded values for the information.
Step 4: Can you combine your small example sketches?
You have code that triggers when the mouse is inside a rectangle. You have code that draws the tooltip. Can you make it so the tooltip is drawn when the mouse is inside the rectangle?
Step 5: Only when all of the above works, then you should start thinking about adding it to your full sketch.
Instead of using an example rectangle, you'll have to use the rectangles you're drawing on the screen. Instead of calling the tooltip function with hard-coded values, use the values you get from the squares.
Take on those pieces one at a time, and make small steps toward your goal. Then if you get stuck, you can post an MCVE of the specific step you're on. Good luck!

Finding inaccessible points on a 2D plane

I have been working on JavaScript / JQuery code which allows arrow key movement between input boxes (yes, I am aware this breaks standard UI).
It works by by looping through each element and finding the closest in each direction (left, right, up and down).
Example
P1:(0, 0), P2:(1, 0), P3:(0, 2)
P1 has one point to the right (P2) and one point up (P3).
P2 has one point to the left (P1) and one point up (P3).
No picture
P3 has two points down (P1 & P2) but P1 is closer.
Therefore the final movements are:
Up
1 -> 3
2 -> 3
Right
1 -> 2
Down
3 -> 1
Left
2 -> 1
For this example:
P1 has two incoming and two outgoing connections.
P2 has one incoming and two outgoing connections.
P3 has two incoming and one outgoing connections.
This made me think.
Is there a set of points such that one or more point is inaccessible (0 incoming connections), or can it be proven no such set exists?
Side note:
If you ignore the up / down component (only using left and right with a vertical split) then point P3 in inaccessible in P1: (0, 0), P2: (2, 0), P3: (1, 4).
Here is the JavaScript / JQuery code if it will help anyone.
function arrowKeyNavigation(elements) {
// Get the position of each element.
var elementOffsets = [];
elements.each(function(key, element) {
elementOffsets[key] = $(element).offset();
});
// Find the closest point in each direction and store the points in data('keyNav') for later use.
for (var i = 0; i < elementOffsets.length; i++) {
var closestPoints = [];
for (var j = 0; j < elementOffsets.length; j++) {
if (i != j) {
var distance = calcDistanceSquared(elementOffsets[i], elementOffsets[j]);
var quadrant = calcQuadrant(elementOffsets[i], elementOffsets[j]);
if (closestPoints[quadrant] == undefined || calcDistanceSquared(elementOffsets[i], elementOffsets[closestPoints[quadrant]]) > distance) {
closestPoints[quadrant] = j;
}
}
}
var closestElements = [];
for (var j = 0; j < closestPoints.length; j++) {
closestElements[j] = elements[closestPoints[j]];
}
$(elements[i]).data('keyNav', closestElements);
}
}
// Returns the distance between two points squared.
function calcDistanceSquared(offset1, offset2) {
...
}
// Returns 0, 1, 2 or 3 for left, up, right and down respectively.
// If a point is EXACTLY 45 degrees it will be classified as either left / right.
function calcQuadrant(offset1, offset2) {
...
}
I've thought about it more and I think I have the solution.
The proof sketch goes as follows:
Assume you have a finite number of points in the plane (R^2). Take an arbitrary point and call it your destination. Then take any other point. That point divides R^2 into four quadrants, as you've drawn in red. By definition the destination is in one of those four quadrants. Move in that direction. One of two things can happen:
1) you arrive at the destination and you're done
2) you move to another point.
If 2, then you've moved closer (EDIT: by the 1-norm distance, d((x1,y1),(x2,y2)) = |x1-x2|+|y1-y2|). This requires more proof but I'm just sketching. The destination is now in some quadrant of this new point.
Now note that If you repeat this, always moving one step closer in the direction of the destination (which may change each step) your distance to the destination point will decrease each step; you can never revisit a point because you distance is always decreasing. So, if you have a finite number of points you'll eventually reach your destination.

Categories