mirror of
https://github.com/Azgaar/Fantasy-Map-Generator.git
synced 2025-12-17 09:41:24 +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
|
|
@ -828,6 +828,7 @@ function parseLoadedData(data) {
|
|||
// v 1.65 changed rivers data
|
||||
d3.select("#rivers").attr("style", null); // remove style to unhide layer
|
||||
const {cells, rivers} = pack;
|
||||
const defaultWidthFactor = rn(1 / (pointsInput.dataset.cells / 10000) ** 0.25, 2);
|
||||
|
||||
for (const river of rivers) {
|
||||
const node = document.getElementById("river" + river.i);
|
||||
|
|
@ -856,7 +857,7 @@ function parseLoadedData(data) {
|
|||
river.points = riverPoints;
|
||||
}
|
||||
|
||||
river.widthFactor = 1;
|
||||
river.widthFactor = defaultWidthFactor;
|
||||
|
||||
cells.i.forEach(i => {
|
||||
const riverInWater = cells.r[i] && cells.h[i] < 20;
|
||||
|
|
|
|||
|
|
@ -23,22 +23,27 @@ window.Rivers = (function () {
|
|||
resolveDepressions(h);
|
||||
drainWater();
|
||||
defineRivers();
|
||||
|
||||
calculateConfluenceFlux();
|
||||
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");
|
||||
|
||||
function drainWater() {
|
||||
const MIN_FLUX_TO_FORM_RIVER = 30;
|
||||
const cellsNumberModifier = (pointsInput.dataset.cells / 10000) ** 0.25;
|
||||
|
||||
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 lakeOutCells = Lakes.setClimateData(h);
|
||||
|
||||
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
|
||||
const lakes = lakeOutCells[i] ? features.filter(feature => i === feature.outCell && feature.flux > feature.evaporation) : [];
|
||||
|
|
@ -90,6 +95,15 @@ window.Rivers = (function () {
|
|||
// cells is depressed
|
||||
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) {
|
||||
// flux is too small to operate as a river
|
||||
if (h[min] >= 20) cells.fl[min] += cells.fl[i];
|
||||
|
|
@ -149,6 +163,9 @@ window.Rivers = (function () {
|
|||
cells.conf = new Uint16Array(cells.i.length);
|
||||
pack.rivers = [];
|
||||
|
||||
const defaultWidthFactor = rn(1 / (pointsInput.dataset.cells / 10000) ** 0.25, 2);
|
||||
const mainStemWidthFactor = defaultWidthFactor * 1.2;
|
||||
|
||||
for (const key in riversData) {
|
||||
const riverCells = riversData[key];
|
||||
if (riverCells.length < 3) continue; // exclude tiny rivers
|
||||
|
|
@ -166,7 +183,7 @@ window.Rivers = (function () {
|
|||
const mouth = riverCells[riverCells.length - 2];
|
||||
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 discharge = cells.fl[mouth]; // m3 in second
|
||||
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() {
|
||||
for (const i of cells.i) {
|
||||
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 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 lengthWidth = pointNumber * STEP_WIDTH + (LENGTH_PROGRESSION[pointNumber] || MAX_PROGRESSION);
|
||||
return widthFactor * (lengthWidth + fluxWidth) + startingWidth;
|
||||
};
|
||||
|
||||
// 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 riverPointsRight = [];
|
||||
|
||||
|
|
@ -444,5 +477,20 @@ window.Rivers = (function () {
|
|||
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() {
|
||||
prec.selectAll("circle").remove();
|
||||
const cells = grid.cells,
|
||||
p = grid.points;
|
||||
const {cells, points} = grid;
|
||||
prec.style("display", "block");
|
||||
const show = d3.transition().duration(800).ease(d3.easeSinIn);
|
||||
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 getRadius = prec => {
|
||||
const base = prec / cellsNumberModifier / 2;
|
||||
return rn(Math.sqrt(base), 2);
|
||||
};
|
||||
|
||||
prec
|
||||
.selectAll("circle")
|
||||
.data(data)
|
||||
.enter()
|
||||
.append("circle")
|
||||
.attr("cx", d => p[d][0])
|
||||
.attr("cy", d => p[d][1])
|
||||
.attr("cx", d => points[d][0])
|
||||
.attr("cy", d => points[d][1])
|
||||
.attr("r", 0)
|
||||
.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) {
|
||||
|
|
|
|||
|
|
@ -89,7 +89,8 @@ function createRiver() {
|
|||
const source = riverCells[0];
|
||||
const mouth = parent === riverId ? last(riverCells) : riverCells[riverCells.length - 2];
|
||||
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);
|
||||
|
||||
|
|
|
|||
|
|
@ -624,7 +624,9 @@ function addRiverOnClick() {
|
|||
|
||||
const source = riverCells[0];
|
||||
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 discharge = cells.fl[mouth]; // m3 in second
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue