Fill a 2d Array with circular area

2019-08-12 18:49发布

I want an array looking like this:

[
[0,0,1,1,1,0,0],
[0,1,1,1,1,1,0],
[1,1,1,1,1,1,1],
[1,1,1,1,1,1,1],
[1,1,1,1,1,1,1],
[0,1,1,1,1,1,0],
[0,0,1,1,1,0,0],
]

My first approach was to get the circumference

var steps = 100;
var coord = [];
var x,y;
for (var i = 0; i < steps; i++) {
    var phase = 2 * Math.PI * i / steps;
    x = Math.round(cenx + range * Math.cos(phase));
    y = Math.round(ceny + range * Math.sin(phase))

    if(x>=0 && y >=0){
        coord.push([x,y]);
    }
}

and with the resulting coords i could have juggled around to get the circular area. but i doubt that would be performant.

So my second approach would be to check every entry of the array whether it has a certain distance (i.e. radius) to the center of my circle. but for huge maps that wouldnt be performant either. perhaps checking only in a reasonable frame would be wiser.

but im certain there is a better approach for this problem. im needing this for a fog of war implementation.

3条回答
闹够了就滚
2楼-- · 2019-08-12 18:51

For an odd-sized array (2r+1 x 2r+1),

for (row= 0; row < 2 * r + 1; row++)
{
  f= (row + 1) * (row - 2 * r - 1) + r * r + r;
  for (col= 0; col < 2 * r + 1; f+= 2 * (col - r) + 1; col++)
  {
    array[row][col]= f >= 0;
  }
}
查看更多
叛逆
3楼-- · 2019-08-12 18:54

Your second suggested approach of testing each point in the array will be simple to implement, and can be optimized to just one subtract, one multiply and one test per element in the inner loop.

The basic test is ((x - centerX) * (x - centerX)) + ((y - centerY) * (y - centerY)) > radiusSq, but since ((y - centerY) * (y - centerY)) will be constant for a given row you can move that outside the loop.

Given that you have to visit each element in the array and set it anyway (meaning your algorithm will always be O(n2) on the circle radius), the test is a negligible cost:

    // circle generation code:
    function makeCircle(centerX, centerY, radius, a, arrayWidth, arrayHeight)
    {
        var x, y, d, yDiff, threshold, radiusSq;
        radius = (radius * 2) + 1;
        radiusSq = (radius * radius) / 4;
        for(y = 0; y < arrayHeight; y++)
        {
            yDiff = y - centerY;
            threshold = radiusSq - (yDiff * yDiff);
            for(x = 0; x < arrayWidth; x++)
            {
                d = x - centerX;
                a[y][x] = ((d * d) > threshold) ? 0 : 1;
            }
        }
    }
    
    // test code:
    var width = 7;
    var dim = (width * 2) + 1;
    var array = new Array(dim);
    for(row = 0; row < dim; row++)
        array[row] = new Array(dim);
    
    makeCircle(width, width, width, array, dim, dim);
    
    for(var y = 0, s = ""; y < dim; y++)
    {
        for(var x = 0; x < dim; x++)
        {
            s += array[y][x];
        }
        s += "<br>";
    }
    document.body.innerHTML += s + "<br>";

查看更多
ゆ 、 Hurt°
4楼-- · 2019-08-12 19:05

I would use the mid-point circle algorithm and see the array as a bitmap.

I did this JavaScript implementation a while back, modified here to use an array as target source for the "pixel". Just note that a circle will produce odd widths and heights as the distance is always from a single center point and we can only use integer values in this case.

Tip: For speed improvements you could use typed array instead of a regular one (shown below).

Example

Make sure to use integer values as input, the code will clip values outside the "bitmap"/array -

var width = 7, height = 7,
    array = new Uint8Array(width * height);

// "draw" circle into array
circle(3, 3, 3);
renderDOM();

// circle example 2
width = height = 17;
array = new Uint8Array(width * height);

circle(8, 8, 8);
renderDOM();

function circle(xc, yc, r) {
  if (r < 1) return;

  var x = r, y = 0,  // for Bresenham / mid-point circle
      cd = 0,
      xoff = 0,
      yoff = r,
      b = -r,
      p0, p1, w0, w1;

  while (xoff <= yoff) {
    p0 = xc - xoff;
    p1 = xc - yoff;
    w0 = xoff + xoff;
    w1 = yoff + yoff;

    hl(p0, yc - yoff, yc + yoff, w0);  // fill a "line"
    hl(p1, yc - xoff, yc + xoff, w1);

    if ((b += xoff+++xoff) >= 0) {
      b -= --yoff + yoff;
    }
  }

  // for fill
  function hl(x, y1, y2, w) {
    w++;
    var xw = 0;
    while (w--) {
      xw = x + w;
      setPixel(xw, y1);
      setPixel(xw, y2);
    }
  }

  function setPixel(x, y) {
	if (x < width && y < height && x >= 0 && y >= 0)
		array[y * width + x] = 1;
  }
}

function renderDOM() {
  for(var i = 0, str = ""; i < array.length; i++) {
    if (i > 0 && !(i % width)) str += "<br>";
    str += array[i];
  }
  document.body.innerHTML += str + "<br><br>";
}
body {font:18px monospace}

查看更多
登录 后发表回答