Index: binaries/data/mods/public/maps/random/rmgen/painter/DunePainter.js =================================================================== --- binaries/data/mods/public/maps/random/rmgen/painter/DunePainter.js +++ binaries/data/mods/public/maps/random/rmgen/painter/DunePainter.js @@ -0,0 +1,51 @@ +/* + *@param {Bool} type - ELEVATION_MODIFY or ELEVATION_SET + *@param {Number} scale - global scale of dune size (width and height) - higher values make dunes bigger + *@param {Number} vertical_scale - scale of dune height - higher makes dunes taller + */ +function DunePainter(type = ELEVATION_MODIFY, scale = 1, vertical_scale = 1) +{ + this.type = type; + this.scale = scale; + this.vertical_scale = vertical_scale; +} +/* + *Dune base shape + *@param {Number} - x [0,1] value + *@param {Number} - xm [0,1] position of the dune crest + *@param {Bool} - d true for dune false for dome + */ +DunePainter.prototype.dune = function(x, crest_x = 0.75, dune = true) +{ + if (x < crest_x) return (1 - Math.cos(Math.PI * x / crest_x)) * 0.5; + if (dune) return (1 - Math.cos(Math.PI * (x - 1) / (crest_x - 1))) * 0.5; + return 1 - Math.cos(Math.PI * (x - 1) / (crest_x - 1) * 0.5); +} + +DunePainter.prototype.paint = function(area) +{ + const points = area.getPoints(); + const length = points.length; + //1st dune layer + const h1_1 = perlin_noise(points, 6, 2, 1, 1.1, true); + const h1_2 = perlin_noise(points, 3, 2, 1, 1, true); + const L1 = 17.0 * this.scale; + //2on dune layer + const h2_1 = perlin_noise(points, 8, 1, 2, 2, true); + const h2_2 = perlin_noise(points, 4, 2, 2, 2, true); + const L2 = 14.0 * this.scale; + //loop all the points + let elevation_type = this.type ? (i) => g_Map.getHeight(points[i]) : () => 0; + for (var i = 0; i < length; i++) + { + //main dunes creation + const v1 = ((points[i].x + h1_1[i] * 13 + points[i].y * 0.1) % L1) / L1; + const h1 = this.dune(v1, 0.60, false) * 9 * 0.9 + h1_1[i] * h1_2[i] * 150 * 0.1; + //secondary dunes + const v2 = ((points[i].x + h2_1[i] * 13 + points[i].y * 0.1) % L2) / L2; + const h2 = this.dune(v2, 0.60, false) * 9 * 0.9 + h2_2[i] * 50 * 0.1; + const height = (h1 * 0.8 + h2 * 0.2) * this.vertical_scale * this.scale; + //add or set height + g_Map.setHeight(points[i], height + elevation_type(i)); + } +} \ No newline at end of file Index: binaries/data/mods/public/maps/random/rmgen/painter/PerlinPainter.js =================================================================== --- binaries/data/mods/public/maps/random/rmgen/painter/PerlinPainter.js +++ binaries/data/mods/public/maps/random/rmgen/painter/PerlinPainter.js @@ -0,0 +1,26 @@ +/* + *@param {Bool} type - ELEVATION_MODIFY or ELEVATION_SET + *@param {Array} points - Points + *@param {Number} frequency - float value >= 1. Noise size, higher value makes noise more compact + *@param {Number} octaves - int value >= 1Sub noise levels, higher the value gives noise more definition **can be as high as desired while the original frequcny doesn't reach < 1 + *@param {Number} scale - Scale size of the perlin noise (in the three axis) + *@param {Number} vertical_scale - Scale size of the perlin noise (in the vertical axis) + *@param {Bool} positive - Returns only on the range of [0,1] (in the case of default paremeters) + */ +function PerlinPainter(type = ELEVATION_MODIFY, frequency = 25, octaves = 10, scale = 1, vertical_scale = 2, positive = true) +{ + this.type = type; + this.frequency = frequency; + this.octaves = octaves; + this.scale = scale; + this.vertical_scale = vertical_scale; + this.positive = positive; +} +PerlinPainter.prototype.paint = function(area) +{ + const points = area.getPoints(); + const length = points.length; + const perlinHeights = perlin_noise(points, this.frequency, this.octaves, this.scale, this.vertical_scale, this.positive); + let elevation_type = this.type ? (i) => g_Map.getHeight(points[i]) : () => 0; + points.forEach( (point,i) => g_Map.setHeight(point, perlinHeights[i] + elevation_type(i) ) ); +} \ No newline at end of file Index: binaries/data/mods/public/maps/random/rmgen/perlin_noise.js =================================================================== --- binaries/data/mods/public/maps/random/rmgen/perlin_noise.js +++ binaries/data/mods/public/maps/random/rmgen/perlin_noise.js @@ -0,0 +1,90 @@ +/* + *Perlin noise. Returns 1D array list of heights for each point given. With default paramenters returns in the range of [-1,1] + *@param {Array} points - Points + *@param {Number} frequency - float value >= 1. Noise size, higher value makes noise more compact + *@param {Number} octaves - int value >= 1Sub noise levels, higher the value gives noise more definition **can be as high as desired while the original frequcny doesn't reach < 1 + *@param {Number} scale - Scale size of the perlin noise (in the three axis) + *@param {Number} vertical_scale - Scale size of the perlin noise (in the vertical axis) + *@param {Bool} positive - Returns only on the range of [0,1] (in the case of default paremeters) + *@return {Array} + */ +function perlin_noise(points, frequency = 10, octaves = 1, scale = 1, vertical_scale = 1, positive = false) +{ + const size = points.length; + let heights = Array(size).fill(0); + frequency /= scale; + let weight = 1; + const frequency_multiplier = 1.333333; + const weight_multiplier = 0.75; + + for (let octave = 0; octave < octaves; octave++) + { + let noise2D = new Noise2D(frequency); + for (let i = 0; i < size; i++) + { + const ix = (points[i].x / 256.0) % 1; + const iy = (points[i].y / 256.0) % 1; + //noise2D gives values between [0.15,0.85] + //transform to a range of [-1,1] + const val = (noise2D.get(ix, iy) - 0.15) / 0.7 * 2 - 1; + heights[i] += val * weight; + } + frequency *= frequency_multiplier; + weight *= weight_multiplier; + } + heights.forEach((v, i, a) => a[i] *= scale * vertical_scale) + + if (positive) + { + const hdisp = vertical_scale * scale * Array(octaves).fill().map((e, i) => Math.pow(weight_multiplier, i + 1)).reduce((a, b) => a + b); + heights.forEach((v, i, a) => a[i] = (v + hdisp) * 0.5) + } + + return heights; +} + +/* + *Perlin noise for individual points + */ +function perlin_noise_point(frequency = 10, octaves = 1, scale = 1, vertical_scale = 1, positive = false) +{ + this.frequency = frequency / scale; + this.octaves = octaves; + this.scale = scale; + this.vertical_scale = vertical_scale; + this.positive = positive; + this.frequency_multiplier = 1.333333; + this.weight_multiplier = 0.75; + let frequency_i = this.frequency; + let weight_i = 1; + + this.noise2D = []; + this.weights = []; + for (let octave = 0; octave < this.octaves; octave++) + { + this.noise2D.push(new Noise2D(frequency_i)) + this.weights.push(weight_i) + frequency_i *= this.frequency_multiplier; + weight_i *= this.weight_multiplier; + } + + this.hdisp = this.vertical_scale * this.scale * Array(this.octaves).fill().map((e, i) => Math.pow(this.weight_multiplier, i + 1)).reduce((a, b) => a + b) +} + +perlin_noise_point.prototype.get = function(point) +{ + let height = 0; + for (let i = 0; i < this.octaves; i++) + { + const x = (point.x / 256.0) % 1; + const y = (point.y / 256.0) % 1; + //noise2D gives values between [0.15,0.85] + //give range [-1,1] + const val = (this.noise2D[i].get(x, y) - 0.15) / 0.7 * 2 - 1; + height += val * this.weights[i]; + } + + height *= this.scale * this.vertical_scale; + + return this.positive ? (height + this.hdisp) * 0.5 : height; +} \ No newline at end of file Index: binaries/data/mods/public/maps/random/rmgen/placer/noncentered/PolygonPlacer.js =================================================================== --- binaries/data/mods/public/maps/random/rmgen/placer/noncentered/PolygonPlacer.js +++ binaries/data/mods/public/maps/random/rmgen/placer/noncentered/PolygonPlacer.js @@ -0,0 +1,93 @@ +/* + *Fills the inside of any polygon given 3 or more points - filling also works with overlapping sections + *Algorithm: https://en.wikipedia.org/wiki/Point_in_polygon + */ +function PolygonPlacer(points) +{ + this.points = points; + + this.horiLine = function(point1, point2) + { + const dy = point2.y - point1.y; + if (dy != 0) + { + this.minx = Math.min(point1.x, point2.x); + this.miny = Math.min(point1.y, point2.y); + this.maxy = Math.max(point1.y, point2.y); + this.val1 = (point2.x - point1.x) / dy; + this.val2 = point1.x - this.val1 * point1.y; + this.doesCross = (point) => this._cross(point); + } + else + { + this.doesCross = (point) => false; + } + } + + this.horiLine.prototype._cross = function(point) + { + //most probable cases + if (point.y > this.maxy) return false; + if (point.y <= this.miny) return false; + //line segment side check + return point.x >= this.val2 + this.val1 * point.y; + } + + this.horiLine.prototype.cross = function(point) + { + return this.doesCross(point); + } + + //bounding box + const plength = this.points.length; + const xlist = this.points.map((v) => v.x).sort((a, b) => a - b); + const ylist = this.points.map((v) => v.y).sort((a, b) => a - b); + this.minx = Math.floor(xlist[0]); + this.miny = Math.floor(ylist[0]); + this.maxx = Math.ceil(xlist[plength - 1]); + this.maxy = Math.ceil(ylist[plength - 1]); + //create lines and sort from min to max for this.minx + this.lines = this.points.map((point, index, arr) => new this.horiLine(point, arr[(index + 1) % plength])).sort((a, b) => a.minx - b.minx) +}; + +PolygonPlacer.prototype.isInside = function(lines, point) +{ + // % 2 returns wheter the number is odd or not + return lines.filter((line) => line.cross(point)).length % 2; +} + +PolygonPlacer.prototype.place = function(constraint) +{ + //check every possible point + let points = []; + //upper right index of lines array + let ix = 0; + const length = this.lines.length; + let lines = []; + for (let x = this.minx; x < this.maxx; x++) + { + //only test for segments positioned to the left of x + for (let i = ix; i < length; ++i) + { + if (this.lines[i].minx > x) + { + ix = i; + lines = this.lines.slice(0, i); + break; + } + } + for (let y = this.miny; y < this.maxy; y++) + { + const point = new Vector2D(x, y); + if (constraint.allows(point)) + { + if (this.isInside(lines, point)) + { + points.push(point); + } + } + } + } + + return points; +}; \ No newline at end of file Index: binaries/data/mods/public/simulation/templates/special/filter/disableGarrisonHolder.xml =================================================================== --- binaries/data/mods/public/simulation/templates/special/filter/disableGarrisonHolder.xml +++ binaries/data/mods/public/simulation/templates/special/filter/disableGarrisonHolder.xml @@ -0,0 +1,4 @@ + + + +