I have a matrix defined mxn 128x128. And I have translated my 2d x,y positions onto this 1D matrix grid. My 2d coordinates accept positions using numbers 0->127 i.e. any combo in ranges {x=0,y=0}-->{x=127,y=127}. I'm implementing algorithms that take the neighboring positions of these nodes. Specifically the 8 surrounding positions of distance i (lets say i=1). So considering node={0,0}, my neighbours are generated by adding these vectors to said node:
two_d_nodes={
{0,i*1},{0,-i*1},{-i*1,0},{i*1,0},
{i*1,i*1},{i*1,-i*1},{-i*1,-i*1},{-i*1,i*1}
}
In terms of 2d though I am excluding neighbours outside the boundary. So in the above for node={0,0}, only neighours {0,1},{1,1}{1,0} are generated. Setting the boundary is basically just implementing some form of:
if x>=0 and y>=0 and x<=127 and y<=127 then...
The 1d translation of node={0,0} is node={0} and my vector additions translated to 1d are:
one_d_nodes={{128},{-128},{-1},{1},{129},{-127},{-129},{127}}
However the relationship with the 2d boundary expressions doesn't hold true here. Or at least I don't know how to translate it. In response I tried generating all the loose cases of the grid:
{0,127,16256,16383} --the 4 corner positions
node%128==0 --right-side boundary
node%128==1 --left-side boundary
node>1 and node<128 --top-side boundary
node>127*128 and node<128*128 --bottom-side boundary
Then tried implementing special cases....where I just ignored generating the specific out of bounds neighbours. That was messy, and didn't even work for some reason. Regardless I feel I am missing a much cleaner method.
So my question is: How do I translate my 2d boundaries onto my 1d grid for the purposes of only generating neighbours within the boundary?
The following is in regards to the answer below:
function newmatrix(node) --node={x=0,y=0}
local matrix={}
add(matrix,{(node.y<<8)+node.x}) --matrix= {{0},...}
--lets say [1 2 3] is a width=3; height=1 matrix,
--then the above line maps my 2d coord to a matrix of width=256, height=128
matrix.height, matrix.width = #node,#node[1] --1,1
return matrix
end
function indexmatrix(matrix, r,c)
if r > 1 and r <= matrix.height and c > 1 and c <= matrix.width then
return matrix[matrix.width * r + c]
else
return false
end
end
function getneighbors(matrix, r, c)
local two_d_nodes={
{0,1},{0,-1},{-1,0},{1,0},
{1,1},{1,-1},{-1,-1},{-1,1}
}
local neighbors = {}
for index, node in ipairs(two_d_nodes) do
table.insert(neighbors, indexmatrix(matrix, r + node[1], c + node[2]))
end
return neighbors
end
--Usage:
m={x=0,y=0}
matrix=newmatrix(m) --{{0}}
--here's where I'm stuck, cause idk what r and c are
--normally I'd grab my neighbors next....
neighbors=getneighbors(matrix)
--then I have indexmatrix for...?
--my understanding is that I am using indexmatrix to
--check if the nieghbors are within the bounds or not, is that right?
--can you illustrate how it would work for my code here, it should
--toss out anything with x less then 0 and y less than 0. Same as in OP's ex
indexmatrix(matrix) ---not sure what to do here
Attempt 2 in regards to the comment sections below:
function indexmatrix(matrix, x ,y)
if x > 1 and x <= matrix['height'] and y > 1 and y <= matrix['width'] then
return matrix[matrix['width'] * x + y]
else
return false
end
end
function getneighbors(matrix, pos_x, pos_y)
local two_d_nodes={
{0,1},{0,-1},{-1,0},{1,0},
{1,1},{1,-1},{-1,-1},{-1,1}
}
local neighbors = {}
for _, node in ipairs(two_d_nodes) do
add(neighbors, indexmatrix(matrix, pos_x + node[1], pos_y + node[2]))
end
return neighbors
end
matrix={} --128 columns/width, 128 rows/height
for k=1,128 do
add(matrix,{}) ----add() is same as table.insert()
for i=1,128 do
matrix[k][i]=i
end
end
id_matrix={{}} --{ {1...16k}}
for j=1,128*128 do
id_matrix[1][j]=j
end
id_matrix.height, id_matrix.width = 128,128
position={x=0,y=0}
neighbors = getNeighbors(matrix, position.x, position.y)
Attempt 3: A working dumbed down version of the code given. Not what I wanted at all.
function indexmatrix(x,y)
if x>=0 and y>=0 and x<127 and y<127 then
return 128 * x + y
else
return false
end
end
function getneighbors(posx,posy)
local two_d_nodes={
{0,1},{0,-1},{-1,0},{1,0},
{1,1},{1,-1},{-1,-1},{-1,1}
}
local neighbors = {}
for _, node in pairs(two_d_nodes) do
add(neighbors, indexmatrix(posx+node[1], posy + node[2]))
end
return neighbors
end
pos={x=0,y=10}
neighbors = getneighbors(pos.x,pos.y)
Edit: The equation to map 2D coordinates to 1D,
y = mx + z
, is a function of two variables. It is not possible for a multivariable equation to have a single solution unless a system of equations is given that getsx
orz
in terms of the other variable. Becausex
andz
are independent of one another, the short answer to the question is: noInstead, the constraints on
x
andz
must be used to ensure integrity of the 1D coordinates.What follows is an example of how to work with a 1D array as if it were a 2D matrix.
Let's say we have a constructor that maps a 2D table to a 1D matrix
The numeric indices are reserved, but we can add non-numeric keys to store information about the matrix. Let's store the number of rows and columns as
height
andwidth
. We can do this in the constructorAlthough the matrix is now a 1x16384 array, we can create a function that allows us to interact with the 1D array like it's still a 2D matrix. This function will get the value of a position in the matrix, but we return
false
/nil
if the indices are out of bounds.To be clear, the formula to map 2D coordinates to a 1D coordinate for a matrix, and can be found here:
And here's what that function could look like:
We can now index our Matrix with any bounds without fear of returning an incorrect element.
Finally, we can make a function to grab the neighbors given a 2D position. In this function, we add vectors to the given 2D position to get surrounding positions, and then index the matrix using the
indexMatrix
function. BecauseindexMatrix
checks if a 2D position is within the bounds of the original Matrix (before it was converted), we only get neighbors that exist.You can either skip elements that return false from
indexMatrix
or remove them after the fact. Or anything else that sounds better to you (this code is not great, it's just meant to be an example). Wrap it in afor i ... do
loop and you can go out an arbitrary distance.I hope I haven't assumed too much and that this is helpful. Just know it's not foolproof (the
#
operator stops counting at the firstnil
, for instance)Edit: Usage