Let's suppose I have the following grid and each cell of the grid has an index mapped to a 1d array.
0, 1, 2
3, 4, 5,
6, 7, 8
I could represent this with a 1d array like: [0, 1, 2, 3, 4, 5, 6, 7, 8]
I would like to know a simple way to map a 2d coordinate like (3,1) to its index in the array, in this case, would be 2.
After researching a lot, I found a lot of people suggesting this equation: index = x + (y * width), but it doesn't seem to work in my tests.
For example for (1, 1), the result would be index = 1 + (1 * 3) = 4, and for (3, 1) would be index = 3 + (1 * 3) = 6, which does not make any sense to me.
Is it possible to achieve this in a simple way? Or I would need to use iterators like a for?
2D matrix notation is commonly (row, col), with indexes starting at 0.
Thus, (3, 1) is invalid: only 3 rows, from 0 to 2. (1, 1) means 2nd row, 2nd colum, which is 4 in your example. The formula is thus:
(row * width) + col
(2, 1) = 2*3+1 = index 7
once again using 0 for the first row/col.
If you really want to keep thinking with indexes starting at one, just change the formula to:
((row - 1) * width) + (col - 1) = 1D index
In your case it would be index = (x - 1) + ((y - 1) * width) as your coordinate system starts from 1 and arrays start from 0.
let arr = [0, 1, 2, 3, 4, 5, 6, 7, 8];
function getPosition(x, y, width) {
return x - 1 + (y - 1) * width;
}
console.log({
position: getPosition(3, 1, 3),
element: arr[getPosition(3, 1, 3)]
});
It is indeed index = x + y * width (the parens are unnecessary) or index = y + x * width, depending on whether you want your flat array to keep the rows together as in your question ([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], index = x + y * width), or keep columns together ([0, 3, 6, 1, 4, 7, 2, 5, 8], index = y + x * width). But indexes usually start at 0, not 1. So your (1, 1) would be (0, 0) and your (3, 1) would be (2, 0).
Here's the first:
// 0, 1, 2
// 3, 4, 5
// 6, 7, 8
const a = [0, 1, 2, 3, 4, 5, 6, 7, 8];
let x = 0, y = 1;
let index = x + y * 3;
console.log(`(${x}, ${y}) is index ${index}, value ${a[index]}`);
x = 2;
y = 0;
index = x + y * 3;
console.log(`(${x}, ${y}) is index ${index}, value ${a[index]}`);
Here's the second:
// 0, 1, 2
// 3, 4, 5
// 6, 7, 8
const a = [0, 3, 6, 1, 4, 7, 2, 5, 8];
let x = 0, y = 1;
let index = y + x * 3;
console.log(`(${x}, ${y}) is index ${index}, value ${a[index]}`);
x = 2;
y = 0;
index = y + x * 3;
console.log(`(${x}, ${y}) is index ${index}, value ${a[index]}`);
Related
So if I have a 2D array such as
const array = [
[1, 2, 3, 4, 5],
[9, 8, 7, 6, 5],
[1, 8, 3, 6, 5],
[9, 8, 7, 6, 5],
[1, 8, 3, 6, 5],
[1, 2, 3, 4, 5]
];
which could for the point of the question be any size, square or not square.
and I want to extract 3x3 arrays out of it, for instance at 1,1 that would be const sub = [[8, 7, 6], [8, 3, 6], [8, 7, 6]]. So far so good - I can do this. However I am flattening the 2D array so that its represented as a 1D array (long story as to why), i.e
const array = [1, 2, 3, 4, 5, 9, 8, 7, 6, 5, 1, 8, 3, 6, 5, 9, 8, 7, 6, 5, 1, 8, 3, 6, 5, 1, 2, 3, 4, 5];
What I'm trying to do is extract the same (or any) 3x3 array out of this but while its represented as a 1D array, so I would then get back [8, 7, 6, 8, 3, 6, 8, 7, 6].
I almost got there, however I made an error of only working with arrays that were 9x9 and always extracting 3x3 subsets which mean that my solution only works for that specific case, and the more I stare at my solution, the more I cannot work out what the generic solution would look like.
My solution:
const extractSubsetFrom1D = (array, subHeight, subWidth, startRow, startCol) => {
const kWH = subWidth * subHeight
const subset = array.slice(((((kWH - 2) * startRow) + startCol) * kWH), ((((kWH - 2) * startRow) + startCol) * kWH) + kWH)
return subset
}
In my case subHeight and subWidth were always equalling 3 respectively, and as the array itself was always 9x9 I believe I accidentally stumbled on a solution for that specific case as they divide nicely into each other.
To be clear my solution will fail for the startRow = 1 startCol = 0 for the provided array (it works for the startRow = 0 scenario
It's not entirely clear to me how you came to your current implementation, but I can at least tell:
✅ You correctly determined the size of the sub grid array to return (kWH)
❌ You incorrectly assume you can slice out a sub grid as one continuous part of the original 1d array
🟠 The calculation of the first element seems kind-of-right but is actually wrong (probably because of the previous mistake)
From (y,x) to i
Let's start from scratch and work our way up to a nice one liner.
In a 2d-array, you can get a cell's value by doing:
cellValue = grid2d[y][x]
Once you flatten it, you'll need to do:
cellValue = grid1d[y * GRID_WIDTH + x]
y * GRID_WIDTH takes you to the start of the right row, and + x gets you to the right column.
As you can see, you need to know the original grid's size before you can even query a specific cell. That means your extract function would need an argument to pass the original width (or, if the grids are guaranteed to be square, you can do Math.sqrt(array.length).
A slice per row
Let's use this math to find the indices of a 2x2 sub grid at (1,1) extracted from a 3x3 source grid:
0 1 2
3 [4][5]
6 [7][8]
As you can see, the resulting indices are [4,5,7,8]. There is no way to slice these indices out of the source array directly because we don't want to include the 6.
Instead, we can use a nested loop to skip the gaps between our rows:
const to1d = (x, y, w) => y * w + x;
const extractSubGrid1D = (grid, gridWidth, x, y, w, h) => {
const yTop = y;
const yBottom = y + h
const xLeft = x;
const xRight = x + w;
const subgrid = [];
for (let y = yTop; y < yBottom; y += 1) {
for (let x = xLeft; x < xRight; x += 1) {
const index = to1d(x, y, gridWidth);
subgrid.push(grid[index]);
}
}
return subgrid;
}
const originalGrid = [
0, 1, 2,
3, 4, 5,
6, 7, 8
];
console.log(
extractSubGrid1D(originalGrid, 3, 1, 1, 2, 2)
)
Once you get a feel for the logic, feel free to refactor.
The other way around
To go from a 1d-index to a 2d coordinate, you can do:
x = i % w
y = Math.floor(i / w)
Applying this logic, you can also fill your sub grid like so:
Create a new array of the right size
For each of its indices, determine the original grid's (x, y) coordinate
Transform that coordinate back to an index to query the original grid with
const to1d = (x, y, w) => y * w + x;
const extractSubGrid1D = (grid, gridWidth, x, y, w, h) => Array.from(
{ length: w * h },
(_, i) => grid[to1d(x + i % w, y + Math.floor(i / w), gridWidth)]
)
const originalGrid = [
0, 1, 2,
3, 4, 5,
6, 7, 8
];
console.log(
extractSubGrid1D(originalGrid, 3, 1, 1, 2, 2)
)
So I'm trying to write a function that can iterate any matrix (square or not) diagonally with configurable left to right or right to left iteration, diagonal along which the matrix is iterated and top to bottom or bottom to top iteration. I did come up with a function that successfully iterates a matrix diagonally left to right, bottom to top along either diagonal
function iterateDiagonally(matrix, main, rightToLeft, topToBottom)
{
for(let a = 0, b = matrix.length + matrix[0].length - 1; a < b; ++a)
{
let x = main ? Math.min(a, matrix.length - 1) : Math.max(0, a - matrix[0].length + 1);
let y = main ? Math.min(matrix[0].length - 1, matrix.length + 1 - a) : Math.min(a, matrix[0].length - 1);
for(let c = 0, d = Math.min(y + 1, main ? x + 1 : matrix.length - x); c < d; ++c)
{
let eX = main ? x - c : x + c;
let eY = y - c;
console.log(matrix[eX][eY]);
}
}
}
/*
1 4 7
2 5 8
3 6 9
*/
console.log("Along the main diagonal");
iterateDiagonally([[1, 2, 3], [4, 5, 6], [7, 8, 9]], true);
console.log("Along the antidiagonal");
iterateDiagonally([[1, 2, 3], [4, 5, 6], [7, 8, 9]], false);
but realized that also making the first 2 parameters variables would make the (already bulky code) look much bulkier. Thus I'm looking for a more clean, compact and no less efficient solution. Thanks!
I've created an alternative implementation for you that's very simple.
Always start on the top row, and either start at the top or bottom depending on the boolean passed in. Then, we begin an iteration loop, always incrementing x, and either incrementing or decrementing y. On each iteration, check whether the point is out of bounds (this is the terminating condition). Then, add the value at that position to the results array.
const board = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
]
const pointInbounds = (x, y, board) => (
x >= 0
&& x < board[0].length
&& y >= 0
&& y < board.length
)
const diagonalTraversal = (board, bottomToTop=false) => {
let y = bottomToTop ? board.length-1 : 0,
x = 0
const values = []
while(pointInbounds(x, y, board)) {
values.push(board[y][x])
x++
y += bottomToTop ? -1 : 1
}
return values
}
diagonalTraversal(board)
By default it will traverse the board from the top left point to the bottom right. If given a true boolean as a second argument, it will start at the bottom left and traverse up to the right.
So I ended up finishing it off myself after a lot of thinking. Leaving it here if anyone comes across the same issue. It can look pretty complicated, but the idea is really simple
function iterateDiagonally(matrix, main, rightToLeft, topToBottom)
{
// Move along half of the perimeter, excluding the duplicate corner element
// a - first element of each diagonal
// b - amount of diagonals in total
for(let a = 0, b = matrix.length + matrix[0].length - 1; a < b; ++a)
{
// For the main diagonal, first move right along the x axis starting with the bottom left element, then move up the y axis
// For the antidiagonal, first move down the y axis starting with the top left element, then move right along the x axis
let x = main ? Math.min(a, matrix.length - 1) : Math.max(0, a - matrix[0].length + 1);
let y = main ? Math.min(matrix[0].length - 1, matrix.length + 1 - a) : Math.min(a, matrix[0].length - 1);
// Invert the starting position and direction of all movement
if(rightToLeft) x = matrix.length - 1 - x, y = matrix[0].length - 1 - y;
let diagonal = [];
// Move along each diagonal
// c - element of diagonal
// d - amount of elements in diagonal in total
for(let c = 0, d = Math.min(rightToLeft ? matrix[0].length - y : y + 1, main !== rightToLeft ? x + 1 : matrix.length - x); c < d; ++c)
{
// Invert the diagonal traversal order
let iC = topToBottom !== rightToLeft ? d - 1 - c : c;
// X coordinate of the element
let eX = main !== rightToLeft ? x - iC : x + iC;
// Y coordinate of the element
let eY = rightToLeft ? y + iC : y - iC;
diagonal.push(matrix[eX][eY]);
}
console.log(diagonal.join(", "));
}
}
/*
1 4 7
2 5 8
3 6 9
*/
console.log("Parallel main diagonal, left to right, bottom to top");
iterateDiagonally([[1, 2, 3], [4, 5, 6], [7, 8, 9]], true, false, false);
console.log("Parallel main diagonal, right to left, bottom to top");
iterateDiagonally([[1, 2, 3], [4, 5, 6], [7, 8, 9]], true, true, false);
console.log("Parallel main diagonal, left to right, top to bottom");
iterateDiagonally([[1, 2, 3], [4, 5, 6], [7, 8, 9]], true, false, true);
console.log("Parallel main diagonal, right to left, top to bottom");
iterateDiagonally([[1, 2, 3], [4, 5, 6], [7, 8, 9]], true, true, true);
console.log("Parallel antidiagonal, left to right, bottom to top");
iterateDiagonally([[1, 2, 3], [4, 5, 6], [7, 8, 9]], false, false, false);
console.log("Parallel antidiagonal, right to left, bottom to top");
iterateDiagonally([[1, 2, 3], [4, 5, 6], [7, 8, 9]], false, true, false);
console.log("Parallel antidiagonal, left to right, top to bottom");
iterateDiagonally([[1, 2, 3], [4, 5, 6], [7, 8, 9]], false, false, true);
console.log("Parallel antidiagonal, right to left, top to bottom");
iterateDiagonally([[1, 2, 3], [4, 5, 6], [7, 8, 9]], false, true, true);
I am currently attempting to display a map in processing using a 2d array.
Currently I have this down:
var start_map = [
[1, 1, 1, 1, 1],
[1, 0, 0, 0, 0],
[1, 0, 0, 0, 0],
[1, 0, 0, 0, 0],
[1, 1, 1, 1, 1]
];
function drawMap(map) {
for (var x = 0; x < 5; x++) {
for (var y = 0; y < 5; y++) {
if (map[x][y] == 0) {
fill(51, 153, 51);
rect((10 + 50*x), (10 + 50*y), 50, 50);
}
else if (map[x][y] == 1) {
fill(0, 102, 0);
rect((10 + 50*x), (10 + 50*y), 50, 50);
}
}
}
}
But, while I do get a map displayed, it appears to be rotated 90 degrees clockwise. What is causing this and how can I fix this?
Think about how the indexes of a 2D array work. Let's look at a simpler example:
var map = [
[1, 2],
[3, 4]
];
Where is map[0][1]? Where is map[1][0]?
A 2D array is an array of arrays. The first index selects the subarray at that index, and the second index selects the element in that subarray.
So in the simple example above, map[0] selects the subarray at index 0, which is [1, 2]. Then map[0][1] selects the element in that subarray at index 1, which is 2.
This might seem a bit surprising if you were treating the indexes as an x, y pair. In that case, you'd expect 0, 1 to give you 3, right? But it's not an x, y pair. It's an index into the outer array, then an index into the subarray.
In other words, it's actually a y, x pair. So to fix your problem, you can actually just swap the order of your indexes:
`map[y][x]`
Now you use the y value to select which subarray you want (which row you want), and the x value to select which element in that subarray you want (which column you want).
How can I fix this code to properly detect overlapping circles?
The first circle is appointed by testing the location of the starting point. This first circle should be the basis of the overlapping circle map. Now, it only works if the tested circles overlap in a non-branching line...
(individual circles come as [x,y,radius])
var circles = [
[6, 19, 1],
[6, 11, 4],
[8, 17, 3],
[19, 19, 2],
[19, 11, 4],
[15, 7, 6],
[12, 19, 4]
];
var i = 0;
var j = 0;
var start = [10, 19];
var starts = false;
var overlapping = [];
var isInside = function(point, list, check, push) {
var temp = list.filter(function(item) { return Math.pow(item[0] - point[0], 2) + Math.pow(item[1] - point[1], 2) < item[2] * item[2] });
if (push) { overlapping = overlapping.concat(temp) };
return temp.length > 0
};
starts = isInside(start, circles, starts, true);
var overlappingCirclesTest = function() {
if (j < circles.length && overlapping.length > 0) {
var i = overlapping.length - 1;
var r0 = overlapping[i][2];
var r1 = circles[j][2];
var x0 = overlapping[i][0];
var x1 = circles[j][0];
var y0 = overlapping[i][1];
var y1 = circles[j][1];
if (Math.hypot(x0 - x1, y0 - y1) <= (r0 + r1)) {
overlapping.push(circles[j]);
circles.splice(circles.indexOf(circles[j]), 1);
j = 0;
overlappingCirclesTest();
}
j++;
overlappingCirclesTest();
}
}
overlappingCirclesTest();
EDIT: for clarification: we have an array of potentially overlapping circles and two points, start and finish. We want to produce a path of overlapping circles, starting with the one with start in it and ending with the one with end in it. There can be several potential paths, we just want to know if there's any path at all.
So here's a very basic collision checking system. Whenever you update, run Collision and pass the parameters of the circle you are checking collision for.
function Collision (x, y, r) {
for (i=0; i<circles.length; i++) {
//Distance formula
if (Math.sqrt((x-circles[i].x)(x-circles[i].x) + (y-circles[i].y)(y-circles[i].y) < r) {
return true;
}
}
Here's an example of a circle object, and how to call it:
function Circle() {
this.x = Math.random()*100;
this.y = Math.random()*100;
this.r = Math.random()*50;
this.update = function() {
if (Collision(this.x, this.y, this.r) {
console.log("circle collided with another circle");
}
}
};
Additionally, you can check out the source of a project I created that uses lots of circles and checks the collision between all of them and the player. http://betaio.bitballoon.com
Here is a more complete answer, I didn't attempt to visualize the circles so its hard for me to be sure this is totally correct but I think this should get you closer.
I think the algorithm is O(N^2) so it won't be fast but the strategy I took is to build up an index over every single overlapping circle and then find one using the point, then essentially recurse through the overlapping index to find all of the cirlces it is associated with in a group.
Here is the code:
function circleCollisionDetect (c1, c2) {
var dx = c1[0] - c2[0]
var dy = c1[1] - c2[1]
var distance = Math.sqrt(dx * dx + dy * dy)
return distance < c1[2] + c2[2]
}
function circlePointCollisionDetect (p, c) {
const dx = p[0] - c[0]
const dy = p[1] - c[1]
const distance = Math.sqrt(dx * dx + dy * dy)
return distance < c[2]
}
function search (i, circles, index) {
const group = []
function follow(i) {
if (!~group.indexOf(i)) {
group.push(i)
const overlaps = index[i]
for (let x = 0, n = overlaps.length; x < n; x++) {
follow(overlaps[x])
}
}
}
follow(i)
return group
}
const circles = [
[6, 19, 1],
[6, 11, 4],
[8, 17, 3],
[19, 19, 2],
[19, 11, 4],
[15, 7, 6],
[12, 19, 4]
]
const overlaps = []
const p = [10, 19]
// Find one that overlaps the starting point
const c = circles.find(c => circlePointCollisionDetect(p, c))
const start = circles.indexOf(c)
// Build an index of all overlapping circles
for (let a = 0, n = circles.length; a < n; a++) {
for (let b = 0; b < n; b++) {
const c1 = circles[a]
const c2 = circles[b]
if (c1 === c2) continue;
if (!overlaps[a]) overlaps[a] = []
if (circleCollisionDetect(c1, c2)) overlaps[a].push(b)
}
}
// Next search through the index recursively for unique overlapping circles
const overlapping = search(start, circles, overlaps)
console.log('start:', start)
console.log('index:', overlaps)
console.log('overlapping:', overlapping)
Which prints:
start: 2
index: [ [ 2 ], [ 2, 5 ], [ 0, 1, 6 ], [], [ 5 ], [ 1, 4 ], [ 2 ] ]
overlapping: [ 2, 0, 1, 5, 4, 6 ]
So basically they are all overlapping each other except for [19, 19, 2], is that correct?
I have 1 to 20 objects inside a canvas.
The objects have left, width, top and height attributes.
I divided my canvas into 12 sections. Each objects width can be 1/12th of the canvas width (month) or 3/12th (quarter).
Now I need to get the sum of the heights of all objects inside each of the 12 grid sections.
For example.
In section 1 is 1 month-object with the height 12 --> result 12
In section 2 are 2 month-objects. heights 10 and 14 --> result 24
In section 4 are 1 month-object and one quarter object: heights 12 and 14 -->result 26
In section 5 are 1 month-object and the same quarter object: heights 4 and 14 --> result 18
Hope this was understandable. Solution is needed in javascript.
What I have is an array with all the beginning sections and all the ending sections.
For example:
startingSections = [1, 2, 2, 4, 4, 5]
endingSections = [1, 2, 2, 4, 6, 5]
My desired output is the following:
var sectionsMax = [];
sectiosnMax = getMonthlyTops();
function getMonthlyTops(){
var monthlyTops = [];
/* Code here */
return monthlyTops;
}
Results could be:
sectionsMax = [12, 24, 0, 26, 18, 14, 0, 0, 0, 0, 0, 0]
Example picture:
Can you try this and post what you get please. Since you can loop through the objects, i dont think you actually need startingSections and endingSections arrays.
var monthlyTops = new Array(0,0,0,0,0,0,0,0,0,0,0,0);
canvas.forEachObject(function (targ) {
x = targ.left / (canvas.width / 12);
for (i = x; i < (x + targ.width / (canvas.width / 12) ); i++)
monthlyTops[i] += targ.height;
});
return monthlyTops;