1
0
forked from mirrors/0ad
Files
0ad/binaries/data/mods/public/maps/random/rmgen/tileclass.js
T
2015-03-15 10:08:16 +00:00

173 lines
3.5 KiB
JavaScript

//////////////////////////////////////////////////////////////////////
// RangeOp
//
// Class for efficiently finding number of points within a range
//
//////////////////////////////////////////////////////////////////////
function RangeOp(size)
{
// Get smallest power of 2 which is greater than or equal to size
this.nn = 1;
while (this.nn < size) {
this.nn *= 2;
}
this.vals = new Int16Array(2*this.nn); // int16
}
RangeOp.prototype.set = function(pos, amt)
{
this.add(pos, amt - this.vals[this.nn + pos]);
};
RangeOp.prototype.add = function(pos, amt)
{
for(var s = this.nn; s >= 1; s /= 2)
{
this.vals[s + pos] += amt;
pos = Math.floor(pos/2);
}
};
RangeOp.prototype.get = function(start, end)
{
var ret = 0;
var i = 1;
var nn = this.nn;
// Count from start to end by powers of 2
for (; start+i <= end; i *= 2)
{
if (start & i)
{ // For each bit in start
ret += this.vals[nn/i + Math.floor(start/i)];
start += i;
}
}
//
while(i >= 1)
{
if(start+i <= end)
{
ret += this.vals[nn/i + Math.floor(start/i)];
start += i;
}
i /= 2;
}
return ret;
};
//////////////////////////////////////////////////////////////////////
// TileClass
//
// Class for representing terrain types and containing all the tiles
// within that type
//
//////////////////////////////////////////////////////////////////////
function TileClass(size, id)
{
this.id = id;
this.size = size;
this.inclusionCount = new Array(size);
this.rangeCount = new Array(size);
for (var i=0; i < size; ++i)
{
this.inclusionCount[i] = new Int16Array(size); //int16
this.rangeCount[i] = new RangeOp(size);
}
}
TileClass.prototype.add = function(x, z)
{
if (!this.inclusionCount[x][z] && g_Map.validT(x, z))
{
this.rangeCount[z].add(x, 1);
}
this.inclusionCount[x][z]++;
};
TileClass.prototype.remove = function(x, z)
{
this.inclusionCount[x][z]--;
if(!this.inclusionCount[x][z])
{
this.rangeCount[z].add(x, -1);
}
};
TileClass.prototype.countInRadius = function(cx, cy, radius, returnMembers)
{
var members = 0;
var nonMembers = 0;
var size = this.size;
var ymax = cy+radius;
var radius2 = radius*radius;
for (var y = cy-radius; y <= ymax; y++)
{
var iy = Math.floor(y);
if (radius >= 27) // Switchover point before RangeOp actually performs better than a straight algorithm
{
if(iy >= 0 && iy < size)
{
var dy = y - cy;
var dx = Math.sqrt(radius*radius - dy*dy);
var lowerX = Math.floor(cx - dx);
var upperX = Math.floor(cx + dx);
var minX = (lowerX > 0 ? lowerX : 0);
var maxX = (upperX < size ? upperX+1 : size);
var total = maxX - minX;
var mem = this.rangeCount[iy].get(minX, maxX);
members += mem;
nonMembers += total - mem;
}
}
else // Simply check the tiles one by one to find the number
{
var dy = (iy - cy);
var xmin = Math.floor(cx - radius);
var xmax = Math.ceil(cx + radius);
xmin = (xmin >= 0) ? xmin : 0;
xmax = ( xmax < size) ? xmax : size - 1;
for (var ix = xmin; ix <= xmax; ++ix)
{
var dx = (ix - cx);
if (dx*dx + dy*dy <= radius2)
{
if (this.inclusionCount[ix] && this.inclusionCount[ix][iy] && this.inclusionCount[ix][iy] > 0)
members += 1;
else
nonMembers += 1;
}
}
}
}
if (returnMembers)
return members;
else
return nonMembers;
};
TileClass.prototype.countMembersInRadius = function(cx, cy, radius)
{
return this.countInRadius(cx, cy, radius, true);
};
TileClass.prototype.countNonMembersInRadius = function(cx, cy, radius)
{
return this.countInRadius(cx, cy, radius, false);
};