Line data Source code
1 : /** 2 : * Generates a more random clump of points. It randomly creates circles around the edges of the current clump.s 3 : * 4 : * @param {number} minRadius - minimum radius of the circles. 5 : * @param {number} maxRadius - maximum radius of the circles. 6 : * @param {number} numCircles - number of circles. 7 : * @param {number} [failFraction] - Percentage of place attempts allowed to fail. 8 : * @param {Vector2D} [centerPosition] 9 : * @param {number} [maxDistance] - Farthest distance from the center. 10 : * @param {number[]} [queue] - When given, uses these radiuses for the first circles. 11 : */ 12 : function ChainPlacer(minRadius, maxRadius, numCircles, failFraction = 0, centerPosition = undefined, maxDistance = 0, queue = []) 13 : { 14 0 : this.minRadius = minRadius; 15 0 : this.maxRadius = maxRadius; 16 0 : this.numCircles = numCircles; 17 0 : this.failFraction = failFraction; 18 0 : this.maxDistance = maxDistance; 19 0 : this.queue = queue.map(radius => Math.floor(radius)); 20 0 : this.centerPosition = undefined; 21 : 22 0 : if (centerPosition) 23 0 : this.setCenterPosition(centerPosition); 24 : } 25 : 26 6 : ChainPlacer.prototype.setCenterPosition = function(position) 27 : { 28 0 : this.centerPosition = deepfreeze(position.clone().round()); 29 : }; 30 : 31 6 : ChainPlacer.prototype.place = function(constraint) 32 : { 33 : // Preliminary bounds check 34 0 : if (!g_Map.inMapBounds(this.centerPosition) || !constraint.allows(this.centerPosition)) 35 0 : return undefined; 36 : 37 0 : let points = []; 38 0 : let size = g_Map.getSize(); 39 0 : let failed = 0; 40 0 : let count = 0; 41 : 42 0 : let gotRet = new Array(size).fill(0).map(p => new Array(size).fill(-1)); 43 0 : --size; 44 : 45 0 : this.minRadius = Math.min(this.maxRadius, Math.max(this.minRadius, 1)); 46 : 47 0 : let edges = [this.centerPosition]; 48 : 49 0 : for (let i = 0; i < this.numCircles; ++i) 50 : { 51 0 : let chainPos = pickRandom(edges); 52 0 : let radius = this.queue.length ? this.queue.pop() : randIntInclusive(this.minRadius, this.maxRadius); 53 0 : let radius2 = Math.square(radius); 54 : 55 0 : let bbox = getPointsInBoundingBox(getBoundingBox([ 56 : new Vector2D(Math.max(0, chainPos.x - radius), Math.max(0, chainPos.y - radius)), 57 : new Vector2D(Math.min(chainPos.x + radius, size), Math.min(chainPos.y + radius, size)) 58 : ])); 59 : 60 0 : for (let position of bbox) 61 : { 62 0 : if (position.distanceToSquared(chainPos) >= radius2) 63 0 : continue; 64 : 65 0 : ++count; 66 : 67 0 : if (!g_Map.inMapBounds(position) || !constraint.allows(position)) 68 : { 69 0 : ++failed; 70 0 : continue; 71 : } 72 : 73 0 : let state = gotRet[position.x][position.y]; 74 0 : if (state == -1) 75 : { 76 0 : points.push(position); 77 0 : gotRet[position.x][position.y] = -2; 78 : } 79 0 : else if (state >= 0) 80 : { 81 0 : edges.splice(state, 1); 82 0 : gotRet[position.x][position.y] = -2; 83 : 84 0 : for (let k = state; k < edges.length; ++k) 85 0 : --gotRet[edges[k].x][edges[k].y]; 86 : } 87 : } 88 : 89 0 : for (let pos of bbox) 90 : { 91 0 : if (this.maxDistance && 92 : (Math.abs(this.centerPosition.x - pos.x) > this.maxDistance || 93 : Math.abs(this.centerPosition.y - pos.y) > this.maxDistance)) 94 0 : continue; 95 : 96 0 : if (gotRet[pos.x][pos.y] != -2) 97 0 : continue; 98 : 99 0 : if (pos.x > 0 && gotRet[pos.x - 1][pos.y] == -1 || 100 : pos.y > 0 && gotRet[pos.x][pos.y - 1] == -1 || 101 : pos.x < size && gotRet[pos.x + 1][pos.y] == -1 || 102 : pos.y < size && gotRet[pos.x][pos.y + 1] == -1) 103 : { 104 0 : edges.push(pos); 105 0 : gotRet[pos.x][pos.y] = edges.length - 1; 106 : } 107 : } 108 : } 109 : 110 0 : return failed > count * this.failFraction ? undefined : points; 111 : };