Search Unity

Loop On Columns On a Flattend 2D Array

Discussion in 'Scripting' started by jGate99, May 14, 2019.

  1. jGate99

    jGate99

    Joined:
    Oct 22, 2013
    Posts:
    1,943
  2. grizzly

    grizzly

    Joined:
    Dec 5, 2012
    Posts:
    357
    You can transform a 2D position to a 1D index like this;
    Code (CSharp):
    1. int GetIndex(int x, int y, int dimension)
    2. {
    3.     return x + y * dimension;
    4. }
    Dimension is the width or height of a square texture.

    So for example, if your texture is 10x10 pixels and your inputs are (x2, y3) then the index will be 2 + 3 * 10 = 32.
     
    jGate99 likes this.
  3. Antistone

    Antistone

    Joined:
    Feb 22, 2014
    Posts:
    2,836
    Grizzly has the correct general idea, but isn't accounting for the fact that you want to invert the Y axis (because you want top-to-bottom, and GetPixels gives you bottom-to-top).

    I believe this will get the pixels in your desired order:

    Code (CSharp):
    1. for (int column = 0; column < TotalColumns; ++column)
    2. {
    3.     for (int row = 0; row < TotalRows; ++row)
    4.     {
    5.         int currentPixelIndex = column + TotalColumns*(TotalRows - row - 1);
    6.         Color currentPixel = pixels[currentPixelIndex];
    7.         // Do stuff
    8.     }
    9. }
     
    jGate99 likes this.
  4. grizzly

    grizzly

    Joined:
    Dec 5, 2012
    Posts:
    357
    In that context it'll be more efficient to just reverse the loop and save the overhead from the added math. :)
     
    jGate99 likes this.
  5. Antistone

    Antistone

    Joined:
    Feb 22, 2014
    Posts:
    2,836
    Marginally. I felt clarity was more important in this discussion. He may need to know how to perform this coordinate conversion for other reasons.
     
    jGate99 likes this.
  6. jGate99

    jGate99

    Joined:
    Oct 22, 2013
    Posts:
    1,943
    Thank you for your reply guys, its an immense help for mathless person like me.

    i have no problem with texture returing from bottom to top, what i want is make lists of indexes of each column

    for example, currently get pixels return like this

    0,1,2,3,4,5,6,7,8

    in 2d, it will look like this (in my mind)
    6,7,8
    3,4,5
    0,1,2


    so i want to get each column in its own array
    so first array
    6
    3
    0
    second array
    7
    4
    1

    and so on.

    so which will be the most efficient way and to do that?
    @Antistone or
    Code (CSharp):
    1.     for (int column = 0; column < TextureWidth; ++column)
    2.     {
    3.         int[] currentColumn = new int[TextureHeight];
    4.  
    5.         for (int row = 0; row < TextureHeight; ++row)
    6.         {
    7.             int currentPixelIndex = column + TextureWidth*(TextureHeight - row - 1);
    8.          
    9.             currentColumn[i] = currentPixelIndex;
    10.         }
    11.      
    12.         //use column here
    13.  
    14.     }
    15.  


    @grizzly ?
    I tried but the above approach but its beyond my head, so if you can assist here.
     
    Last edited: May 15, 2019
  7. jGate99

    jGate99

    Joined:
    Oct 22, 2013
    Posts:
    1,943
    Another problem is, how do i get to upper Cell or Lower Cell in a flattened 2d array.

    in simple 2d array, we can simply y-1 = uper, and y+1 lower, but what about flatted array?
     
  8. grizzly

    grizzly

    Joined:
    Dec 5, 2012
    Posts:
    357
    Your second array example appears to be reading from the middle row? If you meant 1,4,7 for the middle column then the loop would look like this;
    Code (CSharp):
    1. int[,] indices = new int[Width, Height];
    2.  
    3. for (int x = 0; x < Width; ++x)
    4. {
    5.     for (int y = 0; y < Height; ++y)
    6.     {
    7.        indices[x, y] = x + y * Width;          
    8.     }    
    9. }
    10.  
    11. // do something with indices[x, y];
    To reverse the loop simply replace line 5 with this instead;
    Code (CSharp):
    1. for (int y = Height - 1; y >= 0; --y)
     
    jGate99 likes this.
  9. grizzly

    grizzly

    Joined:
    Dec 5, 2012
    Posts:
    357
    The upper bound for 2D is (dimension - 1). For 1D it's (dimension * dimension - 1).
     
  10. Antistone

    Antistone

    Joined:
    Feb 22, 2014
    Posts:
    2,836
    That's why I gave you an explicit conversion from row number and column number to array index; that way, you can manipulate the row and column in any way you want and then just plug the result into the same formula.
     
    jGate99 likes this.
  11. jGate99

    jGate99

    Joined:
    Oct 22, 2013
    Posts:
    1,943
    Thank you guys, really appreciate the help :)
     
    grizzly likes this.
  12. jGate99

    jGate99

    Joined:
    Oct 22, 2013
    Posts:
    1,943
    @grizzly

    Sorry to come back, but (dimension * dimension - 1) is going over my head when i actually start to write it.

    For example 2D arrray
    [0,0][0,1][0,2][0,3]
    [1,0][1,1][1,2][1,3]


    if i have [1,2] i can go to cell right above [1,2] by row-1 = [0,2]

    Now in Flattened Array
    [0][1][2][3][4][5][6][7]

    How do i go from [6] to cell directly above which is [2]?

    - 6x6-1 doesnt work
    - 2x2-1 returns fixed value


    Please advise :(
     
  13. Mordus

    Mordus

    Joined:
    Jun 18, 2015
    Posts:
    174
    Just add/subtract the number of columns.
    Your grid is 4 columns wide. So above/below cell 6 is just 6 +/- 4.
     
    jGate99 likes this.
  14. grizzly

    grizzly

    Joined:
    Dec 5, 2012
    Posts:
    357
    Sorry, ignore that - I'd misinterpreted your question which I understood to be referring to Bounds Checking. If you stick to the original equation I posted you'll be able to read from any row or column.
    Code (CSharp):
    1. int GetIndex(int x, int y, int dimension)
    2. {
    3.     return x + y * dimension;
    4. }
    So with;
    Code (CSharp):
    1. [0,0][0,1][0,2][0,3]
    2. [1,0][1,1][1,2][1,3]
    Here the dimension is 4 (columns wide) and x = columns and y = rows. So the cell [1,2] is column = 2 row = 1. Plug that in and we get;
    Code (CSharp):
    1. 2 + 1 * 4 = index 6
    Therefore for the row "above" we get;
    Code (CSharp):
    1. 2 + 0 * 4 = index 2
     
    jGate99 likes this.
  15. grizzly

    grizzly

    Joined:
    Dec 5, 2012
    Posts:
    357
    I should add that to keep the above answer as clear and concise as possible I've written it to match your example, however, in code the rows in your array would be ordered from bottom to top;
    Code (CSharp):
    1. [1,0][1,1][1,2][1,3]
    2. [0,0][0,1][0,2][0,3]
    And by example;
    Code (CSharp):
    1. int GetIndex(int x, int y, int dimension)
    2. {
    3.     return x + y * dimension;
    4. }
    5.  
    6. var value = array[GetIndex(column, row - 1, 4)];
     
    Last edited: Jun 12, 2019
    jGate99 likes this.
  16. jGate99

    jGate99

    Joined:
    Oct 22, 2013
    Posts:
    1,943
    Thank you very much guys for helping someone with terrible math and sometime explanation.
    Really appreciate.
     
    grizzly likes this.
  17. jGate99

    jGate99

    Joined:
    Oct 22, 2013
    Posts:
    1,943
    Hi Guys, another question,
    i was getting above and below neighbor of a cell in flatted array by adding subtracting columns as you advised which is working fine.

    however when i want to get left and right column then i was done oneDIndex++ (right column) and oneDIndex-- (left column), but looks like its wrong because if my current cell is the first cell of a row then there is no left and it rather goes to below row

    so how do i actually get left cell or right cell?

    Thanks
     
  18. Mordus

    Mordus

    Joined:
    Jun 18, 2015
    Posts:
    174
    Sorry for the late reply, i wasn't checking my forum account for a while :p

    You need to check for the boundaries of the grid when getting neighbors in any direction. If you go 'up'/'down' and there's no neighbour there then you'll get an index out of bounds error when you try to read the array, while if you go 'left'/'right' and there's no neighbour there then you'll wrap around to the next row like you found.

    Taking a 2d index (X, Y co-ordinate) and turning it into a 1d index for storing things in an array can also be reversed and done in the other direction if needed using the % and / operators. You can then use that to check if there even is a neighbour to get. Easier explained with code.
    Code (CSharp):
    1.  
    2. // The size of the 2d grid
    3. int xSize = 10;
    4. int ySize = 15;
    5.  
    6. // A 2d position in the 2d grid
    7. int xPosition = 9;
    8. int yPosition = 7;
    9.  
    10. // Turn the 2d position into a 1d index
    11. int flatIndex = xPosition + (yPosition * xSize);
    12.  
    13. // turn the 1d index back into a 2d position
    14. xPosition = flatIndex % xSize;
    15. yPosition = flatIndex / xSize;
    16.  
    17. // you can use that to do boundary checking when accessing neighbours
    18. // print the indices of each neighbour, if there's a neighbour
    19.  
    20. // Is X - 1 (left) inside the map boundaries
    21. if ((flatIndex % xSize) - 1 >= 0)
    22.     Debug.Log(flatIndex - 1);
    23.  
    24.  
    25. // Is X + 1 (right) inside the map boundaries
    26. if ((flatIndex % xSize) + 1 < xSize)
    27.     Debug.Log(flatIndex + 1);
    28.  
    29. // Is Y - 1 (down) inside the map boundaries
    30. if ((flatIndex / xSize) - 1 >= 0)
    31.     Debug.Log(flatIndex - xSize);
    32.  
    33. // Is Y + 1 (up) inside the map boundaries
    34. if ((flatIndex / xSize) + 1 < ySize)
    35.     Debug.Log(flatIndex + xSize);
    36.  
    37.  
    You'll notice that it only prints 3 numbers. Because 'right' was outside the boundaries of the grid.
     
    jGate99 likes this.
  19. jGate99

    jGate99

    Joined:
    Oct 22, 2013
    Posts:
    1,943
    Thank you very much, you are Math God
    i was doing full conversion earlier but you made it way better and optimize :D