mirror of
https://github.com/Azgaar/Fantasy-Map-Generator.git
synced 2025-12-17 17:51:24 +01:00
refactor: rankCells
This commit is contained in:
parent
3184a29449
commit
d1208b12ec
3 changed files with 74 additions and 34 deletions
|
|
@ -1,9 +1,13 @@
|
||||||
import * as d3 from "d3";
|
import * as d3 from "d3";
|
||||||
|
|
||||||
import {TIME} from "config/logging";
|
import {TIME} from "config/logging";
|
||||||
import {normalize} from "utils/numberUtils";
|
import {normalize, rn} from "utils/numberUtils";
|
||||||
|
import {isWater, isCoastal} from "utils/graphUtils";
|
||||||
|
|
||||||
// assess cells suitability to calculate population and rand cells for culture center and burgs placement
|
const FLUX_MAX_BONUS = 250;
|
||||||
|
const SUITABILITY_FACTOR = 5;
|
||||||
|
|
||||||
|
// assess cells suitability for population and rank cells for culture centers and burgs placement
|
||||||
export function rankCells() {
|
export function rankCells() {
|
||||||
TIME && console.time("rankCells");
|
TIME && console.time("rankCells");
|
||||||
const {cells, features} = pack;
|
const {cells, features} = pack;
|
||||||
|
|
@ -11,36 +15,66 @@ export function rankCells() {
|
||||||
cells.s = new Int16Array(cells.i.length); // cell suitability array
|
cells.s = new Int16Array(cells.i.length); // cell suitability array
|
||||||
cells.pop = new Float32Array(cells.i.length); // cell population array
|
cells.pop = new Float32Array(cells.i.length); // cell population array
|
||||||
|
|
||||||
const flMean = d3.median(cells.fl.filter(f => f)) || 0;
|
const meanFlux = d3.median(cells.fl.filter(f => f)) || 0;
|
||||||
const flMax = d3.max(cells.fl) + d3.max(cells.conf); // to normalize flux
|
const maxFlux = (d3.max(cells.fl) || 0) + (d3.max(cells.conf) || 0); // to normalize flux
|
||||||
const areaMean = d3.mean(cells.area); // to adjust population by cell area
|
const meanArea = d3.mean(cells.area) || 0; // to adjust population by cell area
|
||||||
|
|
||||||
for (const i of cells.i) {
|
for (const cellId of cells.i) {
|
||||||
if (cells.h[i] < 20) continue; // no population in water
|
if (isWater(cellId)) continue; // no population in water
|
||||||
let s = +biomesData.habitability[cells.biome[i]]; // base suitability derived from biome habitability
|
|
||||||
if (!s) continue; // uninhabitable biomes has 0 suitability
|
|
||||||
if (flMean) s += normalize(cells.fl[i] + cells.conf[i], flMean, flMax) * 250; // big rivers and confluences are valued
|
|
||||||
s -= (cells.h[i] - 50) / 5; // low elevation is valued, high is not;
|
|
||||||
|
|
||||||
if (cells.t[i] === 1) {
|
const habitabilityBonus = getHabitabilityBonus(cellId); // [0, 100]
|
||||||
if (cells.r[i]) s += 15; // estuary is valued
|
if (!habitabilityBonus) continue; // uninhabitable biomes are excluded
|
||||||
const feature = features[cells.f[cells.haven[i]]];
|
|
||||||
if (feature.type === "lake") {
|
const riverBonus = getFluxBonus(cellId); // [0, 250]
|
||||||
if (feature.group === "freshwater") s += 30;
|
const elevationBonus = getElevationBonus(cellId); // [-10, 6]
|
||||||
else if (feature.group == "salt") s += 10;
|
const coastBonus = getCoastBonus(cellId); // [-30, 30]
|
||||||
else if (feature.group == "frozen") s += 1;
|
const estuaryBonus = getEstuaryBonus(cellId); // [0, 15]
|
||||||
else if (feature.group == "dry") s -= 5;
|
|
||||||
else if (feature.group == "sinkhole") s -= 5;
|
const suitability =
|
||||||
else if (feature.group == "lava") s -= 30;
|
(habitabilityBonus + riverBonus + elevationBonus + coastBonus + estuaryBonus) / SUITABILITY_FACTOR; // [-30, 311]
|
||||||
} else {
|
|
||||||
s += 5; // ocean coast is valued
|
|
||||||
if (cells.harbor[i] === 1) s += 20; // safe sea harbor is valued
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cells.s[i] = s / 5; // general population rate
|
|
||||||
// cell rural population is suitability adjusted by cell area
|
// cell rural population is suitability adjusted by cell area
|
||||||
cells.pop[i] = cells.s[i] > 0 ? (cells.s[i] * cells.area[i]) / areaMean : 0;
|
const population = suitability > 0 ? suitability * (cells.area[cellId] / meanArea) : 0;
|
||||||
|
|
||||||
|
cells.pop[cellId] = population;
|
||||||
|
cells.s[cellId] = suitability;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getHabitabilityBonus(cellId: number) {
|
||||||
|
return biomesData.habitability[cells.biome[cellId]];
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFluxBonus(cellId: number) {
|
||||||
|
if (!cells.fl[cellId]) return 0;
|
||||||
|
return normalize(cells.fl[cellId] + cells.conf[cellId], meanFlux, maxFlux) * FLUX_MAX_BONUS;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getElevationBonus(cellId: number) {
|
||||||
|
return (50 - cells.h[cellId]) / 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCoastBonus(cellId: number) {
|
||||||
|
if (!isCoastal(cellId)) return 0;
|
||||||
|
|
||||||
|
const havenCell = cells.haven[cellId];
|
||||||
|
const {group} = features[cells.f[havenCell]];
|
||||||
|
|
||||||
|
// lake coast
|
||||||
|
if (group === "freshwater") return 30;
|
||||||
|
if (group == "salt") return 10;
|
||||||
|
if (group == "frozen") return 1;
|
||||||
|
if (group == "dry") return 1;
|
||||||
|
if (group == "sinkhole") return 3;
|
||||||
|
if (group == "lava") return -30;
|
||||||
|
|
||||||
|
// ocean coast
|
||||||
|
if (cells.harbor[cellId] === 1) return 25; // safe harbor
|
||||||
|
return 5; // unsafe harbor
|
||||||
|
}
|
||||||
|
|
||||||
|
// estuary bonus is [0, 15]
|
||||||
|
function getEstuaryBonus(cellId: number) {
|
||||||
|
return cells.r[cellId] && isCoastal(cellId) ? 15 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
TIME && console.timeEnd("rankCells");
|
TIME && console.timeEnd("rankCells");
|
||||||
|
|
|
||||||
4
src/types/pack.d.ts
vendored
4
src/types/pack.d.ts
vendored
|
|
@ -17,6 +17,8 @@ interface IPack {
|
||||||
s: IntArray;
|
s: IntArray;
|
||||||
pop: Float32Array;
|
pop: Float32Array;
|
||||||
fl: UintArray;
|
fl: UintArray;
|
||||||
|
conf: UintArray;
|
||||||
|
r: UintArray;
|
||||||
biome: UintArray;
|
biome: UintArray;
|
||||||
area: UintArray;
|
area: UintArray;
|
||||||
state: UintArray;
|
state: UintArray;
|
||||||
|
|
@ -24,6 +26,8 @@ interface IPack {
|
||||||
religion: UintArray;
|
religion: UintArray;
|
||||||
province: UintArray;
|
province: UintArray;
|
||||||
burg: UintArray;
|
burg: UintArray;
|
||||||
|
haven: UintArray;
|
||||||
|
harbor: UintArray;
|
||||||
q: d3.Quadtree<number[]>;
|
q: d3.Quadtree<number[]>;
|
||||||
};
|
};
|
||||||
states: IState[];
|
states: IState[];
|
||||||
|
|
|
||||||
|
|
@ -160,14 +160,16 @@ export function getGridPolygon(i: number): TPoints {
|
||||||
return grid.cells.v[i].map(v => grid.vertices.p[v]);
|
return grid.cells.v[i].map(v => grid.vertices.p[v]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// filter land cells
|
export function isLand(cellId: number) {
|
||||||
export function isLand(i: number) {
|
return pack.cells.h[cellId] >= 20;
|
||||||
return pack.cells.h[i] >= 20;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// filter water cells
|
export function isWater(cellId: number) {
|
||||||
export function isWater(i: number) {
|
return pack.cells.h[cellId] < 20;
|
||||||
return pack.cells.h[i] < 20;
|
}
|
||||||
|
|
||||||
|
export function isCoastal(i: number) {
|
||||||
|
return pack.cells.t[i] === 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// findAll d3.quandtree search from https://bl.ocks.org/lwthatcher/b41479725e0ff2277c7ac90df2de2b5e
|
// findAll d3.quandtree search from https://bl.ocks.org/lwthatcher/b41479725e0ff2277c7ac90df2de2b5e
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue