Enlarge grid coordinates? - javascript

I've a list of coordinates that define a grid of elements with different sizes.
Each element is positioned starting from the top left corner, giving the starting column, the starting row and its width and height.
{
"col": 1,
"row": 1,
"size_x": 3,
"size_y": 2
},
{
"col": 4,
"row": 1,
"size_x": 3,
"size_y": 2
},
{
"col": 1,
"row": 3,
"size_x": 3,
"size_y": 2
},
{
"col": 4,
"row": 3,
"size_x": 3,
"size_y": 2
},
{
"col": 1,
"row": 5,
"size_x": 3,
"size_y": 2
},
{
"col": 4,
"row": 5,
"size_x": 3,
"size_y": 2
}
This set of coordinates was defined on a 6 columns grid.
Now the grid has 12 columns (and each row is the half of before) so I need to multiply by 2 the size_x and size_y.
But this is not enough, I need also to update the col and row coordinates to prevent collisions and achieve the same layout of the old 6 columns grid.
This is the script I've prepared, it enlarges the size_x and size_y by 2, and fixes the col somehow...
But I can't find a way to complete it and make it reliable.
http://codepen.io/FezVrasta/pen/qbrYGv?editors=001
The layout, once applied to the UI, should look like this:
[---][---]
[---][---]
[---][---]
- = 1 col
[ = 2 rows

I am not 100% clear on the question, but I assume you want a six-column grid to evolve to a 12-column grid, and with some basic Math that is quite easy. I have renamed some variables to what I find comfortable to work with when talking about positioning, but the following logs the grid when it was 12 columns wide, and it will work with any grid you pass it that is structured as an array of objects (which hopefully only contain numbers).
Update I have corrected some stupid mistakes in my script and changed the output and input so you can see them directly in the snippet. Hope it helps!
var grid = [
{ x: 1, y: 1, w: 3, h: 2 },
{ x: 4, y: 1, w: 3, h: 2 },
{ x: 1, y: 3, w: 3, h: 2 },
{ x: 4, y: 3, w: 3, h: 2 },
{ x: 1, y: 5, w: 3, h: 2 },
{ x: 4, y: 5, w: 3, h: 2 }
];
document.getElementById('input').textContent = JSON.stringify(grid);
function resizeGrid(grid, from, to){
// Loop through the grid
for(var i = 0; i < grid.length; i++)
// Loop through the values of the grid
for(var key in grid[i])
// This is a safety check to not enumerate inherited properties
// not necessary, but I find it safer when doing for..in loops
if(grid[i].hasOwnProperty(key))
// Simply divide the value by the original and
// multiply by the desired output
grid[i][key] = grid[i][key] / from * to
return grid;
}
document.getElementById('output').textContent = JSON.stringify(resizeGrid(grid, 6, 12));
<div id="input"></div>
<div id="output"></div>
I am also not 100% sure how you set up your grid, but it might be best to keep your top-left most coordinate as 0 - the reason being that if you have something at 0, 0 it will remain in that corner when you resize the grid - if your coordinates start at 1, this becomes much more complicated as 1,1 will now become 2,2, even though it should not change. Always count from 0!
In order to fix this 1,1 problem, you can simply replace this line:
grid[i][key] = grid[i][key] / from * to;
with this line:
grid[i][key] = grid[i][key] === 1
? 1
: grid[i][key] / from * to;
Heres an implementation:
var grid = [
{ x: 1, y: 1, w: 3, h: 2 },
{ x: 4, y: 1, w: 3, h: 2 }
];
document.getElementById('input').textContent = JSON.stringify(grid);
function resizeGrid(grid, from, to){
// Loop through the grid
for(var i = 0; i < grid.length; i++)
for(var key in grid[i])
if(grid[i].hasOwnProperty(key))
switch(key){
case 'w': case 'h': grid[i][key] = grid[i][key] / from * to; break;
case 'x': case 'y': grid[i][key] = (grid[i][key] - 1) / from * to + 1; break;
}
return grid;
}
document.getElementById('output').textContent = JSON.stringify(resizeGrid(grid, 6, 12));
<div id="input"></div>
<div id="output"></div>

Related

extract 2D array from another 2D array represented as a 1D array

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)
)

Find array index on a 1d array from a 2d grid javascript

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]}`);

Iteration issues with nested for loop

I am having a problem looping through an array of point objects, comparing each object to the other objects, and pushing it to a subarray. The main problem is with the iteration using the for loop. First, I have a set of points:
var points = [
{ id: 1, x: 0.0, y: 0.0 },
{ id: 2, x: 10.1, y: -10.1 },
{ id: 3, x: -12.2, y: 12.2 },
{ id: 4, x: 38.3, y: 38.3 },
{ id: 5, x: 79.0, y: 179.0 }
];
I then want to compare each point to ALL the other points. Apparently, my method is just comparing the i to the j that's next in line in the array. What I want is a subarray for each point object that has the objects id, the id of the point object it's being compared to, and the distance between those 2 points. Ex output: [{1, 2, 12.74423}, {1, 2, 10.76233), {1, 3, 43.23323}, {1, 4, 23.45645}, {1, 5, 127.43432}]; Here is my code, and below that is the output I get in my console. What am I doing wrong here? Note: I put in some random console.logs to see what was going on.
var pointPairs = [];
for (let i = 0; i < points.length; i = i + 1) {
var p1 = points[i];
for (let j = i + 1; j < points.length; j = j + 1) {
var p2 = points[j];
var distance = Math.sqrt((p1.x - p2.x) ** 2 + (p1.y - p2.y) ** 2);
pointPairs.push({ p1: p1.id, p2: p2.id, distance });
console.log(pointPairs);
};
};
Results:
0:{p1: 1, p2: 2, distance: 14.28355697996826}
1:{p1: 1, p2: 3, distance: 17.253405460951758}
2:{p1: 1, p2: 4, distance: 54.16437943888954}
3:{p1: 1, p2: 5, distance: 195.65786465153911}
4:{p1: 2, p2: 3, distance: 31.536962440920014}
5:{p1: 2, p2: 4, distance: 56.01606912306503}
6:{p1: 2, p2: 5, distance: 201.26107422946941}
7:{p1: 3, p2: 4, distance: 56.84593213238745}
8:{p1: 3, p2: 5, distance: 190.10439237429526}
9:{p1: 4, p2: 5, distance: 146.46835835770128}
Pointy is right about needing to start at zero in your second loop.
If you just need to iterate through a list, I recommend avoiding for-loops. They are verbose and error-prone, compared to the built-in array iteration method (Array.forEach). Here is your code, converted to use Array.forEach. I think you'll agree that it's much simpler.
var points = [
{ id: 1, x: 0.0, y: 0.0 },
{ id: 2, x: 10.1, y: -10.1 },
{ id: 3, x: -12.2, y: 12.2 },
{ id: 4, x: 38.3, y: 38.3 },
{ id: 5, x: 79.0, y: 179.0 }
]
var pointPairs = [];
points.forEach((p1, i) => {
points.forEach((p2, j) => {
var distance = Math.sqrt((p1.x - p2.x) ** 2 + (p1.y - p2.y) ** 2);
pointPairs.push({ p1: p1.id, p2: p2.id, distance });
console.log(pointPairs);
})
})
It does appear that your algorithm is working, if the goal is to make a single comparison between any two points - i.e. if p1 and p2 are considered unordered - a combination, not a permutation.
However, if you wish that p1 and p2 contain every possible permutation, not just combination, then start j at 0 and skip the iteration where i===j:
var pointPairs = [];
for (let i = 0; i < points.length; i = i + 1) {
var p1 = points[i];
innerLoop: for (let j = 0; j < points.length; j = j + 1) {
if (j===i) continue innerLoop;
var p2 = points[j];
var distance = Math.sqrt((p1.x - p2.x) ** 2 + (p1.y - p2.y) ** 2);
pointPairs.push({ p1: p1.id, p2: p2.id, distance });
console.log(pointPairs);
};
};
(The innerLoop label is not strictly needed, but helps clarify the code when you have nested loops)

JS / jQuery / Canvas - Array tile map, find value of array in row and column

I'm creating an isometric tile map for a small www game, I have created an array to store map data, column and row numbers aswell as height and width of tile. Then I found function getTile on web which is on the end of this map array. It should be giving me value of the tile later on. It looks like this:
var map = {
cols: 13,
rows: 11,
twidth: 200,
theight: 100,
tiles: [
1, 1, 1, 2, 1, 1, 1, 1, 1, 2, 1, 1, 1,
1, 1, 1, 2, 1, 1, 1, 1, 1, 2, 1, 1, 1,
1, 1, 1, 2, 1, 1, 1, 1, 1, 2, 1, 1, 1,
1, 1, 1, 2, 2, 2, 1, 1, 1, 2, 1, 1, 1,
1, 1, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1,
1, 1, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1,
1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 1, 1, 1,
1, 1, 1, 1, 1, 2, 1, 1, 1, 2, 2, 2, 1,
1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 2, 1,
2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 2, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1,
],
getTile: function(col, row) {
return this.tiles[row * map.cols + col]
}
};
Then I have a code to draw something on the canvas:
for (var c = 0; c < map.cols; c++) {
for (var r = 0; r < map.rows; r++) {
var tile = map.getTile(c, r);
var screenX = (c - r) * map.twidth/2;
var screenY = (c + r) * map.theight/2;
var screenX = screenX+1000;
if(tile == 1) {
var element = "area_grass_01";
}
if(tile == 2) {
var element = "area_road_01";
}
var img = document.getElementById(element);
ctxObj.drawImage(img, (tile - 1) * map.twidth, 0, map.twidth, map.theight, screenX, screenY, map.twidth, map.theight);
}
}
Now, when I run console_log or alert with tile variable in it when the loop is run. It shows all the numbers that are included in map.tiles one by one. However when I try to find out which image should be drawn like this:
if(tile == 1) {
var element = "area_grass_01";
}
if(tile == 2) {
var element = "area_road_01";
}
var img = document.getElementById(element);
It only draws the title with value 1. Rest stays undrawn. Like this:
Map render
Now I want to ask you how do I actually set the image according to the array number I've put inside map.tiles?
And another question that I have to anyone familiar with this. If I have a tile width 200 and height 100, how do I draw let's say a building which is much higher? Do I find the height of that drawing and set it higher by the drawing size - tile height (which is 100), or do you have any other advice on how to draw higher elements? And do I still use the same drawing technique for this:
var screenX = (c - r) * map.twidth/2;
var screenY = (c + r) * map.theight/2;
But with adjustment of map.theight in screenY?
You are using an overload of drawImage of which the second and third parameters are the source X and Y values. You only need to use these if you are making use of a spritesheet which it doesn't seem like you are. Try replacing the second parameter of that function call with just 0.
Take a look at the sx, sy, swidth and sheight parameters here for more information.

How to instantiate and get a length of a multidimensional array / matrix?

How do i instantiate a multidimensional array / matrix in JavaScript and Typescript, and how do i get the matrix length (number of rows) and lengths of the matrix rows?
In Typescript you would instantiate a 2x6 Matrix / Multidimensional array using this syntax:
var matrix: number[][] =
[
[ -1, 1, 2, -2, -3, 0 ],
[ 0.1, 0.5, 0, 0, 0, 0 ]
];
//OR
var matrix: Array<number>[] =
[
[ -1, 1, 2, -2, -3, 0 ],
[ 0.1, 0.5, 0, 0, 0, 0 ]
];
The equivalent in JavaScript would be:
var matrix =
[
[ -1, 1, 2, -2, -3, 0 ],
[ 0.1, 0.5, 0, 0, 0, 0 ]
];
The JavaScript syntax is also valid in TypeScript, since the types are optional, but the TypeScript syntax is not valid in JavaScript.
To get different lengths you would use the same syntax in JS and TS:
var matrixLength = matrix.length; // There are two dimensions, so the length is 2. This is also the column size.
var firstDimensionLength = matrix[0].length; // The first dimension row has 6 elements, so the length is 6
var secondDimensionLength = matrix[1].length; // The second dimension row has 6 elements so the length is 6

Categories

Resources