mirror of
https://github.com/Azgaar/Fantasy-Map-Generator.git
synced 2025-12-17 01:41:22 +01:00
normalize prec, flux and river width
This commit is contained in:
parent
f08b77bd6a
commit
604dcfacf6
6 changed files with 76 additions and 16 deletions
5
main.js
5
main.js
|
|
@ -979,7 +979,10 @@ function generatePrecipitation() {
|
||||||
prec.selectAll("*").remove();
|
prec.selectAll("*").remove();
|
||||||
const {cells, cellsX, cellsY} = grid;
|
const {cells, cellsX, cellsY} = grid;
|
||||||
cells.prec = new Uint8Array(cells.i.length); // precipitation array
|
cells.prec = new Uint8Array(cells.i.length); // precipitation array
|
||||||
const modifier = precInput.value / 100; // user's input
|
|
||||||
|
const cellsNumberModifier = (pointsInput.dataset.cells / 10000) ** 0.25;
|
||||||
|
const precInputModifier = precInput.value / 100;
|
||||||
|
const modifier = cellsNumberModifier * precInputModifier;
|
||||||
|
|
||||||
const westerly = [];
|
const westerly = [];
|
||||||
const easterly = [];
|
const easterly = [];
|
||||||
|
|
|
||||||
|
|
@ -828,6 +828,7 @@ function parseLoadedData(data) {
|
||||||
// v 1.65 changed rivers data
|
// v 1.65 changed rivers data
|
||||||
d3.select("#rivers").attr("style", null); // remove style to unhide layer
|
d3.select("#rivers").attr("style", null); // remove style to unhide layer
|
||||||
const {cells, rivers} = pack;
|
const {cells, rivers} = pack;
|
||||||
|
const defaultWidthFactor = rn(1 / (pointsInput.dataset.cells / 10000) ** 0.25, 2);
|
||||||
|
|
||||||
for (const river of rivers) {
|
for (const river of rivers) {
|
||||||
const node = document.getElementById("river" + river.i);
|
const node = document.getElementById("river" + river.i);
|
||||||
|
|
@ -856,7 +857,7 @@ function parseLoadedData(data) {
|
||||||
river.points = riverPoints;
|
river.points = riverPoints;
|
||||||
}
|
}
|
||||||
|
|
||||||
river.widthFactor = 1;
|
river.widthFactor = defaultWidthFactor;
|
||||||
|
|
||||||
cells.i.forEach(i => {
|
cells.i.forEach(i => {
|
||||||
const riverInWater = cells.r[i] && cells.h[i] < 20;
|
const riverInWater = cells.r[i] && cells.h[i] < 20;
|
||||||
|
|
|
||||||
|
|
@ -23,22 +23,27 @@ window.Rivers = (function () {
|
||||||
resolveDepressions(h);
|
resolveDepressions(h);
|
||||||
drainWater();
|
drainWater();
|
||||||
defineRivers();
|
defineRivers();
|
||||||
|
|
||||||
calculateConfluenceFlux();
|
calculateConfluenceFlux();
|
||||||
Lakes.cleanupLakeData();
|
Lakes.cleanupLakeData();
|
||||||
|
|
||||||
if (allowErosion) cells.h = Uint8Array.from(h); // apply changed heights as basic one
|
if (allowErosion) {
|
||||||
|
cells.h = Uint8Array.from(h); // apply gradient
|
||||||
|
downcutRivers(); // downcut river beds
|
||||||
|
}
|
||||||
|
|
||||||
TIME && console.timeEnd("generateRivers");
|
TIME && console.timeEnd("generateRivers");
|
||||||
|
|
||||||
function drainWater() {
|
function drainWater() {
|
||||||
const MIN_FLUX_TO_FORM_RIVER = 30;
|
const MIN_FLUX_TO_FORM_RIVER = 30;
|
||||||
|
const cellsNumberModifier = (pointsInput.dataset.cells / 10000) ** 0.25;
|
||||||
|
|
||||||
const prec = grid.cells.prec;
|
const prec = grid.cells.prec;
|
||||||
const area = pack.cells.area;
|
|
||||||
const land = cells.i.filter(i => h[i] >= 20).sort((a, b) => h[b] - h[a]);
|
const land = cells.i.filter(i => h[i] >= 20).sort((a, b) => h[b] - h[a]);
|
||||||
const lakeOutCells = Lakes.setClimateData(h);
|
const lakeOutCells = Lakes.setClimateData(h);
|
||||||
|
|
||||||
land.forEach(function (i) {
|
land.forEach(function (i) {
|
||||||
cells.fl[i] += (prec[cells.g[i]] * area[i]) / 100; // add flux from precipitation
|
cells.fl[i] += prec[cells.g[i]] / cellsNumberModifier; // add flux from precipitation
|
||||||
|
|
||||||
// create lake outlet if lake is not in deep depression and flux > evaporation
|
// create lake outlet if lake is not in deep depression and flux > evaporation
|
||||||
const lakes = lakeOutCells[i] ? features.filter(feature => i === feature.outCell && feature.flux > feature.evaporation) : [];
|
const lakes = lakeOutCells[i] ? features.filter(feature => i === feature.outCell && feature.flux > feature.evaporation) : [];
|
||||||
|
|
@ -90,6 +95,15 @@ window.Rivers = (function () {
|
||||||
// cells is depressed
|
// cells is depressed
|
||||||
if (h[i] <= h[min]) return;
|
if (h[i] <= h[min]) return;
|
||||||
|
|
||||||
|
// debug
|
||||||
|
// .append("line")
|
||||||
|
// .attr("x1", pack.cells.p[i][0])
|
||||||
|
// .attr("y1", pack.cells.p[i][1])
|
||||||
|
// .attr("x2", pack.cells.p[min][0])
|
||||||
|
// .attr("y2", pack.cells.p[min][1])
|
||||||
|
// .attr("stroke", "#333")
|
||||||
|
// .attr("stroke-width", 0.2);
|
||||||
|
|
||||||
if (cells.fl[i] < MIN_FLUX_TO_FORM_RIVER) {
|
if (cells.fl[i] < MIN_FLUX_TO_FORM_RIVER) {
|
||||||
// flux is too small to operate as a river
|
// flux is too small to operate as a river
|
||||||
if (h[min] >= 20) cells.fl[min] += cells.fl[i];
|
if (h[min] >= 20) cells.fl[min] += cells.fl[i];
|
||||||
|
|
@ -149,6 +163,9 @@ window.Rivers = (function () {
|
||||||
cells.conf = new Uint16Array(cells.i.length);
|
cells.conf = new Uint16Array(cells.i.length);
|
||||||
pack.rivers = [];
|
pack.rivers = [];
|
||||||
|
|
||||||
|
const defaultWidthFactor = rn(1 / (pointsInput.dataset.cells / 10000) ** 0.25, 2);
|
||||||
|
const mainStemWidthFactor = defaultWidthFactor * 1.2;
|
||||||
|
|
||||||
for (const key in riversData) {
|
for (const key in riversData) {
|
||||||
const riverCells = riversData[key];
|
const riverCells = riversData[key];
|
||||||
if (riverCells.length < 3) continue; // exclude tiny rivers
|
if (riverCells.length < 3) continue; // exclude tiny rivers
|
||||||
|
|
@ -166,7 +183,7 @@ window.Rivers = (function () {
|
||||||
const mouth = riverCells[riverCells.length - 2];
|
const mouth = riverCells[riverCells.length - 2];
|
||||||
const parent = riverParents[key] || 0;
|
const parent = riverParents[key] || 0;
|
||||||
|
|
||||||
const widthFactor = !parent || parent === riverId ? 1.2 : 1;
|
const widthFactor = !parent || parent === riverId ? mainStemWidthFactor : defaultWidthFactor;
|
||||||
const meanderedPoints = addMeandering(riverCells);
|
const meanderedPoints = addMeandering(riverCells);
|
||||||
const discharge = cells.fl[mouth]; // m3 in second
|
const discharge = cells.fl[mouth]; // m3 in second
|
||||||
const length = getApproximateLength(meanderedPoints);
|
const length = getApproximateLength(meanderedPoints);
|
||||||
|
|
@ -176,6 +193,22 @@ window.Rivers = (function () {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function downcutRivers() {
|
||||||
|
const MAX_DOWNCUT = 5;
|
||||||
|
|
||||||
|
for (const i of pack.cells.i) {
|
||||||
|
if (cells.h[i] < 35) continue; // don't donwcut lowlands
|
||||||
|
if (!cells.fl[i]) continue;
|
||||||
|
|
||||||
|
const higherCells = cells.c[i].filter(c => cells.h[c] > cells.h[i]);
|
||||||
|
const higherFlux = higherCells.reduce((acc, c) => acc + cells.fl[c], 0) / higherCells.length;
|
||||||
|
if (!higherFlux) continue;
|
||||||
|
|
||||||
|
const downcut = Math.floor(cells.fl[i] / higherFlux);
|
||||||
|
if (downcut) cells.h[i] -= Math.min(downcut, MAX_DOWNCUT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function calculateConfluenceFlux() {
|
function calculateConfluenceFlux() {
|
||||||
for (const i of cells.i) {
|
for (const i of cells.i) {
|
||||||
if (!cells.conf[i]) continue;
|
if (!cells.conf[i]) continue;
|
||||||
|
|
@ -344,14 +377,14 @@ window.Rivers = (function () {
|
||||||
const LENGTH_PROGRESSION = [1, 1, 2, 3, 5, 8, 13, 21, 34].map(n => n / LENGTH_FACTOR);
|
const LENGTH_PROGRESSION = [1, 1, 2, 3, 5, 8, 13, 21, 34].map(n => n / LENGTH_FACTOR);
|
||||||
const MAX_PROGRESSION = last(LENGTH_PROGRESSION);
|
const MAX_PROGRESSION = last(LENGTH_PROGRESSION);
|
||||||
|
|
||||||
const getOffset = (flux, pointNumber, widthFactor = 1, startingWidth = 0) => {
|
const getOffset = (flux, pointNumber, widthFactor, startingWidth = 0) => {
|
||||||
const fluxWidth = Math.min(flux ** 0.9 / FLUX_FACTOR, MAX_FLUX_WIDTH);
|
const fluxWidth = Math.min(flux ** 0.9 / FLUX_FACTOR, MAX_FLUX_WIDTH);
|
||||||
const lengthWidth = pointNumber * STEP_WIDTH + (LENGTH_PROGRESSION[pointNumber] || MAX_PROGRESSION);
|
const lengthWidth = pointNumber * STEP_WIDTH + (LENGTH_PROGRESSION[pointNumber] || MAX_PROGRESSION);
|
||||||
return widthFactor * (lengthWidth + fluxWidth) + startingWidth;
|
return widthFactor * (lengthWidth + fluxWidth) + startingWidth;
|
||||||
};
|
};
|
||||||
|
|
||||||
// build polygon from a list of points and calculated offset (width)
|
// build polygon from a list of points and calculated offset (width)
|
||||||
const getRiverPath = function (points, widthFactor = 1, startingWidth = 0) {
|
const getRiverPath = function (points, widthFactor, startingWidth = 0) {
|
||||||
const riverPointsLeft = [];
|
const riverPointsLeft = [];
|
||||||
const riverPointsRight = [];
|
const riverPointsRight = [];
|
||||||
|
|
||||||
|
|
@ -444,5 +477,20 @@ window.Rivers = (function () {
|
||||||
return getBasin(parent);
|
return getBasin(parent);
|
||||||
};
|
};
|
||||||
|
|
||||||
return {generate, alterHeights, resolveDepressions, addMeandering, getRiverPath, specify, getName, getType, getBasin, getWidth, getOffset, getApproximateLength, getRiverPoints, remove};
|
return {
|
||||||
|
generate,
|
||||||
|
alterHeights,
|
||||||
|
resolveDepressions,
|
||||||
|
addMeandering,
|
||||||
|
getRiverPath,
|
||||||
|
specify,
|
||||||
|
getName,
|
||||||
|
getType,
|
||||||
|
getBasin,
|
||||||
|
getWidth,
|
||||||
|
getOffset,
|
||||||
|
getApproximateLength,
|
||||||
|
getRiverPoints,
|
||||||
|
remove
|
||||||
|
};
|
||||||
})();
|
})();
|
||||||
|
|
|
||||||
|
|
@ -470,23 +470,28 @@ function togglePrec(event) {
|
||||||
|
|
||||||
function drawPrec() {
|
function drawPrec() {
|
||||||
prec.selectAll("circle").remove();
|
prec.selectAll("circle").remove();
|
||||||
const cells = grid.cells,
|
const {cells, points} = grid;
|
||||||
p = grid.points;
|
|
||||||
prec.style("display", "block");
|
prec.style("display", "block");
|
||||||
const show = d3.transition().duration(800).ease(d3.easeSinIn);
|
const show = d3.transition().duration(800).ease(d3.easeSinIn);
|
||||||
prec.selectAll("text").attr("opacity", 0).transition(show).attr("opacity", 1);
|
prec.selectAll("text").attr("opacity", 0).transition(show).attr("opacity", 1);
|
||||||
|
|
||||||
|
const cellsNumberModifier = pointsInput.dataset.cells / 10000;
|
||||||
const data = cells.i.filter(i => cells.h[i] >= 20 && cells.prec[i]);
|
const data = cells.i.filter(i => cells.h[i] >= 20 && cells.prec[i]);
|
||||||
|
const getRadius = prec => {
|
||||||
|
const base = prec / cellsNumberModifier / 2;
|
||||||
|
return rn(Math.sqrt(base), 2);
|
||||||
|
};
|
||||||
|
|
||||||
prec
|
prec
|
||||||
.selectAll("circle")
|
.selectAll("circle")
|
||||||
.data(data)
|
.data(data)
|
||||||
.enter()
|
.enter()
|
||||||
.append("circle")
|
.append("circle")
|
||||||
.attr("cx", d => p[d][0])
|
.attr("cx", d => points[d][0])
|
||||||
.attr("cy", d => p[d][1])
|
.attr("cy", d => points[d][1])
|
||||||
.attr("r", 0)
|
.attr("r", 0)
|
||||||
.transition(show)
|
.transition(show)
|
||||||
.attr("r", d => rn(Math.max(Math.sqrt(cells.prec[d] * 0.5), 0.8), 2));
|
.attr("r", d => getRadius(cells.prec[d]));
|
||||||
}
|
}
|
||||||
|
|
||||||
function togglePopulation(event) {
|
function togglePopulation(event) {
|
||||||
|
|
|
||||||
|
|
@ -89,7 +89,8 @@ function createRiver() {
|
||||||
const source = riverCells[0];
|
const source = riverCells[0];
|
||||||
const mouth = parent === riverId ? last(riverCells) : riverCells[riverCells.length - 2];
|
const mouth = parent === riverId ? last(riverCells) : riverCells[riverCells.length - 2];
|
||||||
const sourceWidth = 0.05;
|
const sourceWidth = 0.05;
|
||||||
const widthFactor = 1.2;
|
const defaultWidthFactor = rn(1 / (pointsInput.dataset.cells / 10000) ** 0.25, 2);
|
||||||
|
const widthFactor = 1.2 * defaultWidthFactor;
|
||||||
|
|
||||||
const meanderedPoints = addMeandering(riverCells);
|
const meanderedPoints = addMeandering(riverCells);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -624,7 +624,9 @@ function addRiverOnClick() {
|
||||||
|
|
||||||
const source = riverCells[0];
|
const source = riverCells[0];
|
||||||
const mouth = riverCells[riverCells.length - 2];
|
const mouth = riverCells[riverCells.length - 2];
|
||||||
const widthFactor = river?.widthFactor || (!parent || parent === riverId ? 1.2 : 1);
|
|
||||||
|
const defaultWidthFactor = rn(1 / (pointsInput.dataset.cells / 10000) ** 0.25, 2);
|
||||||
|
const widthFactor = river?.widthFactor || (!parent || parent === riverId ? defaultWidthFactor * 1.2 : defaultWidthFactor);
|
||||||
const meanderedPoints = addMeandering(riverCells);
|
const meanderedPoints = addMeandering(riverCells);
|
||||||
|
|
||||||
const discharge = cells.fl[mouth]; // m3 in second
|
const discharge = cells.fl[mouth]; // m3 in second
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue