Log In  

First post on the forum for this PICO-8 newbie.

I'm trying to find a way to store data in a 3D array and it seems to work by itself but when I try to implement it in more advanced code I start getting errors about referencing a nil value. I got the idea from this reddit thread and thought if I wanted to have multiple variables stored for each point on the grid I could just add a 3rd array. This is what I ended up with:

    pixelgrid = {}
    for row = 1, 43 do
        pixelgrid[row] = {}
        for column = 1, 43 do
            pixelgrid[row][column] = {}
            for zpos = 1, 3 do
                pixelgrid[row][column][zpos] = 0
            end
        end
    end

But then if I try to edit a value like so:

    for x = left, left + discsize do
        for y = top, top + discsize do
            pixelgrid[x][y][1] = px
            pixelgrid[x][y][2] = py
            pixelgrid[x][y][3] = magsq
        end
    end

I get an error indicating that pixelgrid[x][y][1] wasn't initliased.

So I'm just wondering have I got this whole 3D array idea wrong or is it more likely something is going wrong elsewhere in the code that is causing my x and y values to not line up with the array and "miss" the variables I defined?

P#51558 2018-04-12 22:39 ( Edited 2018-04-15 07:17)

Hope this helps:

-- multidimensional arrays
a = {{1,2,3}, {4,5,6}, {7,8,9}}

-- print part of array
print (a[1][1])

-- print at screen location
print (a[1][1],10,10)
-- return cursor
cursor (0,20)

-- print all of array
for y = 1, 3 do
for x = 1, 3 do
print(a[y][x])
end
end

-- determine size
print (#a)

-- for additional information see - http://blog.headchant.com/arrays-in-pico-8/

P#51559 2018-04-12 23:21 ( Edited 2018-04-13 03:21)

My guess is that you're accidentally accessing outside of the valid range.

Try this:

    for x = left, left + discsize do
        for y = top, top + discsize do
_x=x
_y=y
            pixelgrid[x][y][1] = px
            pixelgrid[x][y][2] = py
            pixelgrid[x][y][3] = magsq
        end
    end

When it crashes, print the debug globals _x and _y at the command prompt and see if they're outside 1..43.

P#51562 2018-04-12 23:44 ( Edited 2018-04-13 03:44)

As an aside, why exactly are you creating this array? Like, what's the purpose? Sometimes that helps us readers offer ideas for alternate methods that may work better, if there are any.

P#51563 2018-04-12 23:48 ( Edited 2018-04-13 03:48)

Thanks for the quick responses!

I think I overcomplicated the question. Just to be clear I'm not trying to create 3D for graphics, I only want to store more than one variable in each cell of my 2D array so I assumed a third array would achieve this.

Let's say I have a grid of data (my 2D array) that I can look up by x and y. If I call grid[x][y] I will get the value of that cell on the grid but I also want to be able to specify a third argument like grid[x][y][z] to choose from mutiple values stored in that cell on the grid. So x = which column the data is on, y = which row the data is on and z = which variable I want to retrieve from that cell.

Am I making this difficult for myself? Should I just create a seperate 2D array for each variable instead?

@Felice I'm not at my computer right now but I'll post the specific example when I get home.

P#51566 2018-04-13 00:01 ( Edited 2018-04-13 04:10)
1

No, what you're doing totally makes sense.

All you're really doing anyway is creating tables whose members are also tables. You can do this all the way down to an out-of-memory error--there's no limit.

Sounds like what you're wanting is a 2D array of tables with values related to the 2D position. What you have is basically that, though I think the last layer might be better off using named members than numbered ones:

    pixelgrid = {}
    for row = 1, 43 do
        pixelgrid[row] = {}
        for column = 1, 43 do
            pixelgrid[row][column] =
            {
                px = 0,
                py = 0,
                magsq = 0
            }
        end
    end

    -- to set up an entry specifically
    pixelgrid[x][y].px = px
    pixelgrid[x][y].py = py
    pixelgrid[x][y].magsq = magsq

It's just a matter of aesthetics/readability though.

P#51568 2018-04-13 00:14 ( Edited 2018-04-13 04:14)

What are the values you are using for left and discsize? Is the left variable 1-based like the array? Is discsize bigger than 43?

Not able to check at the moment but I think if the x dimension of the table hasn't been initialised, then regardless of whether y and z are in range it won't work? Then same applies if y isn't initialised and you try to access z. Hope that makes sense.

P#51569 2018-04-13 02:43 ( Edited 2018-04-13 06:43)

So I was trying to use the code I found here in PICO-8 and I got it working with a spinning globe but the CPU was at 96%. There was a lot of math that was being done multiple times in the same step despite the result not changing so I was trying to initialise these values at boot and just read them from memory instead of doing the math every time. For each x,y location I needed 3 variables; px, py and magsq. The original code without the array is here:

function _init()

    left = 42
    top = 42
    discsize = 43
    t = 0
    rotationspeed = .5
    mapheight = 32

end

function _draw()

    cls()

    t+=1

    for x = left, left + discsize do
        for y = top, top + discsize do

            px = (x - left) * 2/discsize - 1
            py = (y - top) * 2/discsize - 1

            magsq = px * px + py * py

            scale = 0.35 * magsq + (1 - 0.35)
            px = px * scale
            py = py * scale

            u = t * rotationspeed + (px + 1) * (mapheight/2) 
            v = (py + 1) * (mapheight/2)
            u = u % (2 * mapheight)

            color = sget(u, v)
            pset(x, y, color)

            if ( magsq > 1 ) then
                pset(x, y, 0)
            end

        end
    end
    print("cpu = "..stat(1),8,8,7)
end

And after using Felice's debugging method I found my that my code was accessing a cell just outside of the range I defined. So I added one more cell to each axis of the array and got it all working the way I wanted. Here is the end result:

function _init()

    left = 42
    top = 42
    discsize = 43
    t = 0
    rotationspeed = .5
    mapheight = 32

    pixelgrid = {}
    for row = 1, 44 do
        pixelgrid[row] = {}
        for column = 1, 44 do
            pixelgrid[row][column] =
            {
                px = 0,
                py = 0,
                magsq = 0
            }
        end
    end

    for x = left, left + discsize do
        for y = top, top + discsize do

            px = (x - left) * 2/discsize - 1
            py = (y - top) * 2/discsize - 1

            magsq = px * px + py * py

            scale = 0.35 * magsq + (1 - 0.35)
            px = px * scale
            py = py * scale

            _x = x - left + 1
            _y = y - top + 1

            pixelgrid[x-left+1][y-top+1].px = px
            pixelgrid[x-left+1][y-top+1].py = py
            pixelgrid[x-left+1][y-top+1].magsq = magsq

        end
    end

end

function _draw()

    cls()

    t+=1

    for x = left, left + discsize do
        for y = top, top + discsize do

            u = t * rotationspeed + (pixelgrid[x-left+1][y-top+1].px + 1) * (mapheight/2) 
            v = (pixelgrid[x-left+1][y-top+1].py + 1) * (mapheight/2)

            u = u % (2 * mapheight)

            color = sget(u, v)
            pset(x, y, color)

            if ( pixelgrid[x-left+1][y-top+1].magsq > 1 ) then
                pset(x, y, 0)
            end

        end
    end
    print("cpu = "..stat(1),8,8,7)
end

Unfortunately not as much of a performance boost as I was hoping for but at least I know I wasn't barking up the wrong tree by nesting arrays in arrays. I'm sure that will come in handy again later.

P#51571 2018-04-13 03:51 ( Edited 2018-04-13 07:51)

you have a lot of redundant dereferencing.
using locals should provide a little boost:

for x = left, left + discsize do
    local pixelgrid_x=pixelgrid[x-left+1]
    for y = top, top + discsize do
        local pixelgrid_xy=pixelgrid_x[y-top+1]
        u = t * rotationspeed + (pixelgrid_xy.px + 1) * (mapheight/2) 
        v = (pixelgrid_xy.py + 1) * (mapheight/2)

        u = u % (2 * mapheight)

        color = sget(u, v)
        pset(x, y, color)

        if (pixelgrid_xy.magsq > 1 ) then
            pset(x, y, 0)
        end
    end
end
P#51573 2018-04-13 06:08 ( Edited 2018-04-13 10:08)

@ultrabrite: Thanks for the tip!

With that optimization the CPU dropped all the way down to 54% but I don't fully understand how it works...

What exactly do you mean by redundant dereferencing? Does that just mean that I'm accessing global variables more often than I need to?

Correct me if I'm wrong but it looks like you are copying the 3D array into two local variables so pixelgrid_x contains the y array for the current x coordinate and pixelgrid_xy contains the variables for the current x,y coordinates. My guess is that the boost in performance is coming from the fact that we are only referencing the global variable once per row and once per column as opposed to looking it up every time we move to the next cell.

Now that I've typed that out I feel like I might have answered my own question but I'm still keen to see if my guess is correct :P

Another related question though... Can anyone see anything else that can be moved out of the _update function? I'm wondering if I might be able to create another 3D array for variables with predictable outcomes but at first glance it seems like the remaining code in _update needs to be updated constantly due to the fact that the rotation is changing the sprite lookup coordinates on every frame. Thoughts?

P#51615 2018-04-14 21:33 ( Edited 2018-04-15 01:33)

you got that right! plus there's no copying, pixelgrid_x is a reference/pointer to the current y-indexed table.

the next thing to do is move constant calculation out of the loops.
for instance:

local half_mapheight=mapheight/2
local uu = t * rotationspeed +  half_mapheight

for x...
    for y...
        ...
        u = uu + pixelgrid_xy.px*half_mapheight
        ...
    end
end
P#51621 2018-04-15 02:47 ( Edited 2018-04-15 06:47)
2

Nice demo effect!

You are already doing a good deal of pre-calculation, but you can squeeze some more:

  • use locals as much as possible (slight perf boost)
  • look at each computation in your inner loop and see if it can moved outside (eg. mapheight*/2)

By applying these simple rules, I am now down to 22%.

Cart #51627 | 2018-04-15 | Code ▽ | Embed ▽ | No License
2

P#51622 2018-04-15 03:03 ( Edited 2018-04-15 07:19)

@ultrabrite: dang, missed the time*speed constant!!
(at least, we agree on the recommendations :) )

P#51625 2018-04-15 03:17 ( Edited 2018-04-15 07:20)

[Please log in to post a comment]

Follow Lexaloffle:          
Generated 2024-03-28 11:32:53 | 0.025s | Q:33