// Copyright (C) 2020 smiley // SPDX-License-Identifier: GPL-2.0-or-later /* Each metaball is defined as a function in n dimensions f(x, y, .., ..) The sum of f(x, y, .., ..) of each metaball being greater than a set threshold decides if the point is filled or not. https://en.wikipedia.org/wiki/Metaballs Could be a reasonable faster alternative to the current two placers */ function Metaball(pos, radius) { // center coords this.x = pos.x; this.y = pos.y; //g_Map.setTexture(new Vector2D(x, y).round(), "yellow"); this.radius = radius } Metaball.prototype.func = function(x, y) { return this.radius / Math.sqrt((x - this.x) * (x - this.x) + (y - this.y) * (y - this.y)); }; function Metaballs(size, threshold, centerPos) { this.size = size; this.threshold = threshold; this.centerPos = centerPos; } Metaballs.prototype.place = function(constraints) { let points = []; let numballs = Math.floor(this.size / 3); warn(numballs); let metaballs = new Array(numballs).fill(0).map(p => new Metaball(new Vector2D(this.centerPos.x + randFloat(-10, 10), this.centerPos.y + randFloat(-10, 10)), 1.5)); for (let x = 0; x < g_Map.getSize(); ++x) { for (let y = 0; y < g_Map.getSize(); ++y) { let sum = 0; for (let mball of metaballs) sum += mball.func(x, y); if (sum < this.threshold) continue; let point = new Vector2D(x, y); if (constraints.allows(point)) points.push(point); } } return points; }; function DensityGradientPainter(templateName, playerID, minDistance, maxDistance, centroidNum, startingPoint, direction) { this.templateName = templateName; this.playerID = playerID; this.minDistance = minDistance; this.maxDistance = maxDistance; this.centroidNum = centroidNum; this.direction = direction; // One can add a gradient that goes from an edge towards a direction this.startingPoint = startingPoint; if ((this.direction || this.startingPoint) && this.centroidNum) throw new Error("You cannot have centroids with gradient defined from a point and a direction"); } DensityGradientPainter.prototype.paint = function(area) { let points = area.getPoints(); if (this.centroidNum) { let centroids = new Array(this.centroidNum).fill(0).map(p => new Metaball(pickRandom(points), 2)); for (let point of points) { let distanceVal = 0; for (let centroid of centroids) distanceVal += centroid.func(point.x, point.y); warn(distanceVal); if (randBool(distanceVal/1.3)) g_Map.placeEntityPassable(this.templateName, this.playerID, point, randomAngle()); } } else { let point2 = this.direction.perpendicular().add(this.startingPoint); // forms an edge from which points will measure their distance for (let point of points) { let dist = distanceOfPointFromLine(this.startingPoint, point2, point); let distProbability = randIntInclusive(3, 10) / dist; // hackkkkkyyyy if (randBool(distProbability)) g_Map.placeEntityPassable(this.templateName, this.playerID, point, randomAngle()); } } };