GMLscripts.com

Discuss and collaborate on GML scripts
Invert

You are not logged in.

#1 2013-09-13 16:32:07

xot
Administrator
Registered: 2007-08-18
Posts: 1,239

ds_grid_set_grid_disk()

Nocturne recently made a request to YoYo for a ds_grid_set_grid_disk() function that works like ds_grid_set_grid_region(). I agreed that it would be a good opportunity to fill an obvious gap in the ds_grid feature set. The request was denied.

So I bashed together a GML implementation. The original version was a bit slicker and used sprite-like masking tricks to copy the data between grids. Things took an unexpected turn when testing with GM:Studio revealed a bug with the Windows implementation of the ds_grid_set_grid_region(). When the xpos,ypos parameters are both negative, the wrong cells are modified.

That meant recoding it with a more straight-forward approach which is what is presented below. It might be slower (I've not actually timed them) but it works. I've tested it carefully in an effort to make it behave as closely as possible to the existing ds_grid_*_disk functions.

I've also made versions for addition and multiplication to round things out. They have not been tested to the same degree, but they each only differ by one line of code.

Expand//  ds_grid_set_grid_disk(id,source,xm,ym,r,xpos,ypos)
//
//      id          destination grid
//      source      source grid
//      xm,ym       disk center in the source grid
//      r           disk radius
//      xpos,ypos   disk center in the destination grid
//
//  Notes: Set destination grid cells to the source grid 
//  cell values in a given disk-shaped area. Source and
//  destination may be the same grid.
//
//  GMLscripts.com
{
    var grid,source,xm,ym,r,xpos,ypos;
    grid = argument0;
    source = argument1;
    xm = argument2;
    ym = argument3;
    r = argument4;
    xpos = argument5;
    ypos = argument6;
  
    var w,h;
    w = ds_grid_width(source);
    h = ds_grid_height(source);
    
    var copy;
    copy = ds_grid_create(w,h);
    ds_grid_copy(copy,source);
    
    var mask;
    mask = ds_grid_create(w,h);
    ds_grid_set_disk(mask,xm,ym,r,1);
    
    var x0,x1,y0,y1,dx,dy;
    x0 = max(0,ceil(xm-r));
    x1 = min(w-1,floor(xm+r));
    y0 = max(0,ceil(ym-r));
    y1 = min(h-1,floor(ym+r));
    dx = xpos-xm;
    dy = ypos-ym;
    
    var i,ii,j,jj;
    for (i=x0; i<=x1; i+=1) {
        ii = round(i+dx);
        if (ii < 0 || ii >= w) continue;
        for (j=y0; j<=y1; j+=1) {
            jj = round(j+dy);
            if (jj < 0 || jj >= h) continue;
            if (ds_grid_get(mask,i,j))
            {
                ds_grid_set(grid,ii,jj,ds_grid_get(copy,i,j));
            }
        }
    }
    ds_grid_destroy(mask);
    ds_grid_destroy(copy);
}
Expand//  ds_grid_add_grid_disk(id,source,xm,ym,r,xpos,ypos)
//
//      id          destination grid
//      source      source grid
//      xm,ym       disk center in the source grid
//      r           disk radius
//      xpos,ypos   disk center in the destination grid
//
//  Notes: Add to destination grid cells the source grid 
//  cell values in a given disk-shaped area. Source and
//  destination may be the same grid.
//
//  GMLscripts.com
{
    var grid,source,xm,ym,r,xpos,ypos;
    grid = argument0;
    source = argument1;
    xm = argument2;
    ym = argument3;
    r = argument4;
    xpos = argument5;
    ypos = argument6;
  
    var w,h;
    w = ds_grid_width(source);
    h = ds_grid_height(source);
    
    var copy;
    copy = ds_grid_create(w,h);
    ds_grid_copy(copy,source);
    
    var mask;
    mask = ds_grid_create(w,h);
    ds_grid_set_disk(mask,xm,ym,r,1);
    
    var x0,x1,y0,y1,dx,dy;
    x0 = max(0,ceil(xm-r));
    x1 = min(w-1,floor(xm+r));
    y0 = max(0,ceil(ym-r));
    y1 = min(h-1,floor(ym+r));
    dx = xpos-xm;
    dy = ypos-ym;
    
    var i,ii,j,jj;
    for (i=x0; i<=x1; i+=1) {
        ii = round(i+dx);
        if (ii < 0 || ii >= w) continue;
        for (j=y0; j<=y1; j+=1) {
            jj = round(j+dy);
            if (jj < 0 || jj >= h) continue;
            if (ds_grid_get(mask,i,j))
            {
                ds_grid_add(grid,ii,jj,ds_grid_get(copy,i,j));
            }
        }
    }
    ds_grid_destroy(mask);
    ds_grid_destroy(copy);
}
Expand//  ds_grid_multiply_grid_disk(id,source,xm,ym,r,xpos,ypos)
//
//      id          destination grid
//      source      source grid
//      xm,ym       disk center in the source grid
//      r           disk radius
//      xpos,ypos   disk center in the destination grid
//
//  Notes: Multiply destination grid cells by the source grid 
//  cell values in a given disk-shaped area. Source and
//  destination may be the same grid.
//
//  GMLscripts.com
{
    var grid,source,xm,ym,r,xpos,ypos;
    grid = argument0;
    source = argument1;
    xm = argument2;
    ym = argument3;
    r = argument4;
    xpos = argument5;
    ypos = argument6;
  
    var w,h;
    w = ds_grid_width(source);
    h = ds_grid_height(source);
    
    var copy;
    copy = ds_grid_create(w,h);
    ds_grid_copy(copy,source);
    
    var mask;
    mask = ds_grid_create(w,h);
    ds_grid_set_disk(mask,xm,ym,r,1);
    
    var x0,x1,y0,y1,dx,dy;
    x0 = max(0,ceil(xm-r));
    x1 = min(w-1,floor(xm+r));
    y0 = max(0,ceil(ym-r));
    y1 = min(h-1,floor(ym+r));
    dx = xpos-xm;
    dy = ypos-ym;
    
    var i,ii,j,jj;
    for (i=x0; i<=x1; i+=1) {
        ii = round(i+dx);
        if (ii < 0 || ii >= w) continue;
        for (j=y0; j<=y1; j+=1) {
            jj = round(j+dy);
            if (jj < 0 || jj >= h) continue;
            if (ds_grid_get(mask,i,j))
            {
                ds_grid_multiply(grid,ii,jj,ds_grid_get(copy,i,j));
            }
        }
    }
    ds_grid_destroy(mask);
    ds_grid_destroy(copy);
}

Abusing forum power since 1986.

Offline

#2 2013-09-13 19:55:01

icuurd12b42
Member
Registered: 2008-12-11
Posts: 303

Re: ds_grid_set_grid_disk()

Good stuff. subtract and divide can be derived from those too.

Offline

#3 2013-09-25 17:11:29

brac37
Member
Registered: 2010-02-12
Posts: 18

Re: ds_grid_set_grid_disk()

The scripts seem a little inefficient to me. The dx and dy can be rounded, so that you do not need to round later. But there is no need to use ii and jj in the loops. Instead, you can adapt x0,x1,y0,y1 such that i + dx and j + dy are always within the destination. The use of a grid copy seems unnecessary, because it is not modified, so that the source can be used directly.

For numerical grids, things can be done more efficiently. For instance, setting can be done by first multiplying the mask by source, next multiplying the destination by the anti-mask, and at last adding the multiplied mask to the destination.

Although operations with both strings and numbers do not seem to work with grids, you can multiply a number by a string to get the string repeated. Now that you are looping anyway, it is an option to use * instead of ds_grid_multiply, so that string grids can be multiplied by numerical grids at some disk.

Offline

#4 2013-09-25 20:55:28

xot
Administrator
Registered: 2007-08-18
Posts: 1,239

Re: ds_grid_set_grid_disk()

There is a lot of subtly to the rounding behavior so I went with what was most straight-forward after many attempts at being clever and failing to get things exactly right. If you want to offer alternatives, knock yourself out. I'm tired of working on these but I'm happy to test anything new.

That said, adapting the {x0,y0,x1,y1} bounds is probably prudent. If I change anything it will be that.

I purposefully used ds_grid_add/multiply to ensure that it behaves exactly like the existing functions. Also it is worth noting that operand order is significant when attempting to multiply mixed types. I don't want to deal with special cases and error trapping.

What you suggest for numerical grids is exactly what I did originally and was inspired by sprite masking. It works well, except for the bugged case GM:Studio grid routines. Attempting to work around the bug by limiting the regions resulted in rounding problems I wasn't able to work out. I might post the GM:S-bug-encumbered version later -- more likely if the bug is fixed -- but I still prefer a script that is universally applicable even if it isn't as fast.


Abusing forum power since 1986.

Offline

Board footer

Powered by FluxBB