0ad/binaries/data/mods/public/maps/random/rmgen/pathplacer.js
Spahbod 9c34b1ffed Major random map bugfixes:
Fixes the chicken problem
There are no more unreachable trees around the map (hopefully)
Fixed the bug that caused edges of the map ruin the layout of painters
(One of the best examples to look is archipelago, before and after this
revision).
Fixed those out of memory errors caused by pathplacer (Mostly noticeable
in alpine valley and corinthian isthmus)
And some other minor tweaks.
Fixes #1577

This was SVN commit r13004.
2012-12-21 09:48:36 +00:00

211 lines
5.5 KiB
JavaScript

/////////////////////////////////////////////////////////////////////////////////////////
// PathPlacer
//
// Class for creating a winding path between two points
//
// x1,z1: Starting point of path
// x2,z2: Ending point of path
// width: Width of the path
// a: Waviness - how wavy the path will be (higher is wavier, 0.0 is straight line)
// b: Smoothness - how smooth the path will be (higher is smoother)
// c: Offset - max amplitude of waves along the path (0.0 is straight line)
// taper: Tapering - how much the width of the path changes from start to end
// if positive, the width will decrease by that factor, if negative the width
// will increase by that factor
//
/////////////////////////////////////////////////////////////////////////////////////////
function PathPlacer(x1, z1, x2, z2, width, a, b, c, taper, failfraction)
{
this.x1 = x1;
this.z1 = z1;
this.x2 = x2;
this.z2 = z2;
this.width = width;
this.a = a;
this.b = b;
this.c = c;
this.taper = taper;
this.failfraction = (failfraction !== undefined ? failfraction : 5);
}
PathPlacer.prototype.place = function(constraint)
{
/*/ Preliminary bounds check
if (!g_Map.validT(this.x1, this.z1) || !constraint.allows(this.x1, this.z1) ||
!g_Map.validT(this.x2, this.z2) || !constraint.allows(this.x2, this.z2))
{
return undefined;
}*/
var failed = 0;
var dx = (this.x2 - this.x1);
var dz = (this.z2 - this.z1);
var dist = Math.sqrt(dx*dx + dz*dz);
dx /= dist;
dz /= dist;
var numSteps = 1 + Math.floor(dist/4 * this.a);
var numISteps = 1 + Math.floor(dist/4 * this.b);
var totalSteps = numSteps*numISteps;
var offset = 1 + Math.floor(dist/4 * this.c);
var size = getMapSize();
var gotRet = new Array(size);
for (var i = 0; i < size; ++i)
{
gotRet[i] = new Uint8Array(size); // bool / uint8
}
// Generate random offsets
var ctrlVals = new Float32Array(numSteps); //float32
for (var j = 1; j < (numSteps-1); ++j)
{
ctrlVals[j] = randFloat(-offset, offset);
}
// Interpolate for smoothed 1D noise
var noise = new Float32Array(totalSteps+1); //float32
for (var j = 0; j < numSteps; ++j)
{
// Cubic interpolation
var v0 = ctrlVals[(j+numSteps-1)%numSteps];
var v1 = ctrlVals[j];
var v2 = ctrlVals[(j+1)%numSteps];
var v3 = ctrlVals[(j+2)%numSteps];
var P = (v3 - v2) - (v0 - v1);
var Q = (v0 - v1) - P;
var R = v2 - v0;
var S = v1;
for (var k = 0; k < numISteps; ++k)
{
var t = k/numISteps;
noise[j*numISteps + k] = P*t*t*t + Q*t*t + R*t + S;
}
}
var halfWidth = 0.5 * this.width;
// Add smoothed noise to straight path
var segments1 = [];
var segments2 = [];
for (var j = 0; j < totalSteps; ++j)
{
// Interpolated points along straight path
var t = j/totalSteps;
var tx = this.x1 * (1.0 - t) + this.x2 * t;
var tz = this.z1 * (1.0 - t) + this.z2 * t;
var t2 = (j+1)/totalSteps;
var tx2 = this.x1 * (1.0 - t2) + this.x2 * t2;
var tz2 = this.z1 * (1.0 - t2) + this.z2 * t2;
// Find noise offset points
var nx = (tx - dz * noise[j]);
var nz = (tz + dx * noise[j]);
var nx2 = (tx2 - dz * noise[j+1]);
var nz2 = (tz2 + dx * noise[j+1]);
// Find slope of offset points
var ndx = (nx2 - nx);
var ndz = (nz2 - nz);
var dist = Math.sqrt(ndx*ndx + ndz*ndz);
ndx /= dist;
ndz /= dist;
var taperedWidth = (1.0 - t*this.taper) * halfWidth;
// Find slope of offset path
var px = Math.round(nx - ndz * -taperedWidth);
var pz = Math.round(nz + ndx * -taperedWidth);
segments1.push(new PointXZ(px, pz));
var px2 = Math.round(nx2 - ndz * taperedWidth);
var pz2 = Math.round(nz2 + ndx * taperedWidth);
segments2.push(new PointXZ(px2, pz2));
}
var retVec = [];
// Draw path segments
var num = segments1.length - 1;
for (var j = 0; j < num; ++j)
{
// Fill quad formed by these 4 points
// Note the code assumes these points have been rounded to integer values
var pt11 = segments1[j];
var pt12 = segments1[j+1];
var pt21 = segments2[j];
var pt22 = segments2[j+1];
var tris = [[pt12, pt11, pt21], [pt12, pt21, pt22]];
for (var t = 0; t < 2; ++t)
{
// Sort vertices by min z
var tri = tris[t].sort(
function(a, b)
{
return a.z - b.z;
}
);
// Fills in a line from (z, x1) to (z,x2)
var fillLine = function(z, x1, x2)
{
var left = Math.round(Math.min(x1, x2));
var right = Math.round(Math.max(x1, x2));
for (var x = left; x <= right; x++)
{
if (constraint.allows(x, z))
{
if (g_Map.inMapBounds(x, z) && !gotRet[x][z])
{
retVec.push(new PointXZ(x, z));
gotRet[x][z] = 1;
}
}
else
{
failed++;
}
}
}
var A = tri[0];
var B = tri[1];
var C = tri[2];
var dx1 = (B.z != A.z) ? ((B.x - A.x) / (B.z - A.z)) : 0;
var dx2 = (C.z != A.z) ? ((C.x - A.x) / (C.z - A.z)) : 0;
var dx3 = (C.z != B.z) ? ((C.x - B.x) / (C.z - B.z)) : 0;
if (A.z == B.z)
{
fillLine(A.z, A.x, B.x);
}
else
{
for (var z = A.z; z <= B.z; z++)
{
fillLine(z, A.x + dx1*(z - A.z), A.x + dx2*(z - A.z));
}
}
if (B.z == C.z)
{
fillLine(B.z, B.x, C.x);
}
else
{
for (var z = B.z + 1; z < C.z; z++)
{
fillLine(z, B.x + dx3*(z - B.z), A.x + dx2*(z - A.z));
}
}
}
}
return ((failed > this.width*this.failfraction*dist) ? undefined : retVec);
}