mirror of
https://github.com/Azgaar/Fantasy-Map-Generator.git
synced 2025-12-17 09:41:24 +01:00
river type - fix fork type selection
This commit is contained in:
parent
27d460ce2f
commit
50f497c62a
5 changed files with 58 additions and 34 deletions
|
|
@ -80,6 +80,7 @@
|
||||||
TIME && console.time("createStates");
|
TIME && console.time("createStates");
|
||||||
const states = [{i: 0, name: "Neutrals"}];
|
const states = [{i: 0, name: "Neutrals"}];
|
||||||
const colors = getColors(burgs.length - 1);
|
const colors = getColors(burgs.length - 1);
|
||||||
|
const each5th = each(5);
|
||||||
|
|
||||||
burgs.forEach(function (b, i) {
|
burgs.forEach(function (b, i) {
|
||||||
if (!i) return; // skip first element
|
if (!i) return; // skip first element
|
||||||
|
|
@ -93,7 +94,7 @@
|
||||||
|
|
||||||
// states data
|
// states data
|
||||||
const expansionism = rn(Math.random() * powerInput.value + 1, 1);
|
const expansionism = rn(Math.random() * powerInput.value + 1, 1);
|
||||||
const basename = b.name.length < 9 && b.cell % 5 === 0 ? b.name : Names.getCultureShort(b.culture);
|
const basename = b.name.length < 9 && each5th(b.cell) ? b.name : Names.getCultureShort(b.culture);
|
||||||
const name = Names.getState(basename, b.culture);
|
const name = Names.getState(basename, b.culture);
|
||||||
const type = cultures[b.culture].type;
|
const type = cultures[b.culture].type;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -422,7 +422,6 @@
|
||||||
if (d % 6 !== 0) return;
|
if (d % 6 !== 0) return;
|
||||||
for (const l of d3.range(i)) {
|
for (const l of d3.range(i)) {
|
||||||
const min = cells.c[cur][d3.scan(cells.c[cur], (a, b) => cells.h[a] - cells.h[b])]; // downhill cell
|
const min = cells.c[cur][d3.scan(cells.c[cur], (a, b) => cells.h[a] - cells.h[b])]; // downhill cell
|
||||||
//debug.append("circle").attr("cx", p[min][0]).attr("cy", p[min][1]).attr("r", 1);
|
|
||||||
cells.h[min] = (cells.h[cur] * 2 + cells.h[min]) / 3;
|
cells.h[min] = (cells.h[cur] * 2 + cells.h[min]) / 3;
|
||||||
cur = min;
|
cur = min;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -171,14 +171,14 @@
|
||||||
|
|
||||||
const widthFactor = !parent || parent === riverId ? 1.2 : 1;
|
const widthFactor = !parent || parent === riverId ? 1.2 : 1;
|
||||||
const initStep = cells.h[source] >= 20 ? 1 : 10;
|
const initStep = cells.h[source] >= 20 ? 1 : 10;
|
||||||
const riverMeandered = addMeandering(riverCells, initStep, 0.5);
|
const riverMeandered = addMeandering(riverCells, initStep);
|
||||||
const [path, length, offset] = getRiverPath(riverMeandered, widthFactor);
|
const [path, length, offset] = getRiverPath(riverMeandered, widthFactor);
|
||||||
riverPaths.push([path, riverId]);
|
riverPaths.push([path, riverId]);
|
||||||
|
|
||||||
// Real mouth width examples: Amazon 6000m, Volga 6000m, Dniepr 3000m, Mississippi 1300m, Themes 900m,
|
// Real mouth width examples: Amazon 6000m, Volga 6000m, Dniepr 3000m, Mississippi 1300m, Themes 900m,
|
||||||
// Danube 800m, Daugava 600m, Neva 500m, Nile 450m, Don 400m, Wisla 300m, Pripyat 150m, Bug 140m, Muchavets 40m
|
// Danube 800m, Daugava 600m, Neva 500m, Nile 450m, Don 400m, Wisla 300m, Pripyat 150m, Bug 140m, Muchavets 40m
|
||||||
const width = rn((offset / 1.4) ** 2, 2); // mouth width in km
|
const width = rn((offset / 1.4) ** 2, 2); // mouth width in km
|
||||||
const discharge = cells.fl[mouth]; // in m3/s
|
const discharge = cells.fl[mouth]; // m3 in second
|
||||||
|
|
||||||
pack.rivers.push({i: riverId, source, mouth, discharge, length, width, widthFactor, sourceWidth: 0, parent, cells: riverCells});
|
pack.rivers.push({i: riverId, source, mouth, discharge, length, width, widthFactor, sourceWidth: 0, parent, cells: riverCells});
|
||||||
}
|
}
|
||||||
|
|
@ -269,11 +269,12 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
// add points at 1/3 and 2/3 of a line between adjacents river cells
|
// add points at 1/3 and 2/3 of a line between adjacents river cells
|
||||||
const addMeandering = function (riverCells, step = 1, meandering = 0.5) {
|
const addMeandering = function (riverCells, step = 1, meandering = 0.5, riverPoints = null) {
|
||||||
|
const {fl, conf} = pack.cells;
|
||||||
const meandered = [];
|
const meandered = [];
|
||||||
const {p, fl, conf} = pack.cells;
|
|
||||||
|
|
||||||
const lastStep = riverCells.length - 1;
|
const lastStep = riverCells.length - 1;
|
||||||
|
const points = getRiverPoints(riverCells, riverPoints);
|
||||||
|
|
||||||
let fluxPrev = 0;
|
let fluxPrev = 0;
|
||||||
const getFlux = (step, flux) => (step === lastStep ? fluxPrev : flux);
|
const getFlux = (step, flux) => (step === lastStep ? fluxPrev : flux);
|
||||||
|
|
||||||
|
|
@ -281,22 +282,21 @@
|
||||||
const cell = riverCells[i];
|
const cell = riverCells[i];
|
||||||
const isLastCell = i === lastStep;
|
const isLastCell = i === lastStep;
|
||||||
|
|
||||||
const [x1, y1] = p[cell];
|
const [x1, y1] = points[i];
|
||||||
const flux1 = getFlux(i, fl[cell]);
|
const flux1 = getFlux(i, fl[cell]);
|
||||||
fluxPrev = flux1;
|
fluxPrev = flux1;
|
||||||
|
|
||||||
meandered.push([x1, y1, flux1]);
|
meandered.push([x1, y1, flux1]);
|
||||||
|
|
||||||
if (isLastCell) break;
|
if (isLastCell) break;
|
||||||
|
|
||||||
const nextCell = riverCells[i + 1];
|
const nextCell = riverCells[i + 1];
|
||||||
|
const [x2, y2] = points[i + 1];
|
||||||
|
|
||||||
if (nextCell === -1) {
|
if (nextCell === -1) {
|
||||||
const [x, y] = getBorderPoint(cell);
|
meandered.push([x2, y2, fluxPrev]);
|
||||||
meandered.push([x, y, fluxPrev]);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
const [x2, y2] = p[nextCell];
|
|
||||||
const dist2 = (x2 - x1) ** 2 + (y2 - y1) ** 2; // square distance between cells
|
const dist2 = (x2 - x1) ** 2 + (y2 - y1) ** 2; // square distance between cells
|
||||||
if (dist2 <= 25 && riverCells.length >= 6) continue;
|
if (dist2 <= 25 && riverCells.length >= 6) continue;
|
||||||
|
|
||||||
|
|
@ -328,6 +328,24 @@
|
||||||
return meandered;
|
return meandered;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getRiverPoints = (riverCells, riverPoints) => {
|
||||||
|
const {p} = pack.cells;
|
||||||
|
return riverCells.map((cell, i) => {
|
||||||
|
if (riverPoints && riverPoints[i]) return riverPoints[i];
|
||||||
|
if (cell === -1) return getBorderPoint(riverCells[i - 1]);
|
||||||
|
return p[cell];
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const getBorderPoint = i => {
|
||||||
|
const [x, y] = pack.cells.p[i];
|
||||||
|
const min = Math.min(y, graphHeight - y, x, graphWidth - x);
|
||||||
|
if (min === y) return [x, 0];
|
||||||
|
else if (min === graphHeight - y) return [x, graphHeight];
|
||||||
|
else if (min === x) return [0, y];
|
||||||
|
return [graphWidth, y];
|
||||||
|
};
|
||||||
|
|
||||||
const fluxFactor = 500;
|
const fluxFactor = 500;
|
||||||
const maxFluxWidth = 2;
|
const maxFluxWidth = 2;
|
||||||
const widthFactor = 200;
|
const widthFactor = 200;
|
||||||
|
|
@ -373,13 +391,11 @@
|
||||||
Math.random = aleaPRNG(seed);
|
Math.random = aleaPRNG(seed);
|
||||||
const thresholdElement = Math.ceil(rivers.length * 0.15);
|
const thresholdElement = Math.ceil(rivers.length * 0.15);
|
||||||
const smallLength = rivers.map(r => r.length || 0).sort((a, b) => a - b)[thresholdElement];
|
const smallLength = rivers.map(r => r.length || 0).sort((a, b) => a - b)[thresholdElement];
|
||||||
const smallType = {Creek: 9, River: 3, Brook: 3, Stream: 1}; // weighted small river types
|
|
||||||
|
|
||||||
for (const r of rivers) {
|
for (const r of rivers) {
|
||||||
r.basin = getBasin(r.i);
|
r.basin = getBasin(r.i);
|
||||||
r.name = getName(r.mouth);
|
r.name = getName(r.mouth);
|
||||||
const small = r.length < smallLength;
|
r.type = getType(r, r.length < smallLength);
|
||||||
r.type = r.parent && !(r.i % 6) ? (small ? "Branch" : "Fork") : small ? rw(smallType) : "River";
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -387,6 +403,22 @@
|
||||||
return Names.getCulture(pack.cells.culture[cell]);
|
return Names.getCulture(pack.cells.culture[cell]);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// weighted arrays of river type names
|
||||||
|
const riverTypes = {
|
||||||
|
main: {
|
||||||
|
big: {River: 1},
|
||||||
|
small: {Creek: 9, River: 3, Brook: 3, Stream: 1}
|
||||||
|
},
|
||||||
|
fork: {
|
||||||
|
big: {Fork: 1},
|
||||||
|
small: {Branch: 1}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const getType = function (river, isSmall) {
|
||||||
|
const isFork = each(3)(river.i) && river.parent && river.parent !== river.i;
|
||||||
|
return rw(riverTypes[isFork ? "fork" : "main"][isSmall ? "small" : "big"]);
|
||||||
|
};
|
||||||
|
|
||||||
// remove river and all its tributaries
|
// remove river and all its tributaries
|
||||||
const remove = function (id) {
|
const remove = function (id) {
|
||||||
const cells = pack.cells;
|
const cells = pack.cells;
|
||||||
|
|
@ -407,14 +439,5 @@
|
||||||
return getBasin(parent);
|
return getBasin(parent);
|
||||||
};
|
};
|
||||||
|
|
||||||
const getBorderPoint = i => {
|
|
||||||
const [x, y] = pack.cells.p[i];
|
|
||||||
const min = Math.min(y, graphHeight - y, x, graphWidth - x);
|
|
||||||
if (min === y) return [x, 0];
|
|
||||||
else if (min === graphHeight - y) return [x, graphHeight];
|
|
||||||
else if (min === x) return [0, y];
|
|
||||||
return [graphWidth, y];
|
|
||||||
};
|
|
||||||
|
|
||||||
return {generate, alterHeights, resolveDepressions, addMeandering, getPath: getRiverPath, specify, getName, getBasin, remove};
|
return {generate, alterHeights, resolveDepressions, addMeandering, getPath: getRiverPath, specify, getName, getBasin, remove};
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -534,12 +534,12 @@ function addRiverOnClick() {
|
||||||
const point = d3.mouse(this);
|
const point = d3.mouse(this);
|
||||||
let i = findCell(point[0], point[1]);
|
let i = findCell(point[0], point[1]);
|
||||||
|
|
||||||
if (cells.r[i]) return tip("There already a river here", false, "error");
|
if (cells.r[i]) return tip("There is already a river here", false, "error");
|
||||||
if (cells.h[i] < 20) return tip("Cannot create river in water cell", false, "error");
|
if (cells.h[i] < 20) return tip("Cannot create river in water cell", false, "error");
|
||||||
if (cells.b[i]) return;
|
if (cells.b[i]) return;
|
||||||
|
|
||||||
const riverCells = [];
|
const riverCells = [];
|
||||||
let riverId = +getNextId("river").slice(5);
|
let riverId = last(rivers).id + 1;
|
||||||
let parent = 0;
|
let parent = 0;
|
||||||
|
|
||||||
const initialFlux = grid.cells.prec[cells.g[i]];
|
const initialFlux = grid.cells.prec[cells.g[i]];
|
||||||
|
|
@ -555,8 +555,6 @@ function addRiverOnClick() {
|
||||||
const min = cells.c[i].sort((a, b) => h[a] - h[b])[0]; // downhill cell
|
const min = cells.c[i].sort((a, b) => h[a] - h[b])[0]; // downhill cell
|
||||||
if (h[i] <= h[min]) return tip(`Cell ${i} is depressed, river cannot flow further`, false, "error");
|
if (h[i] <= h[min]) return tip(`Cell ${i} is depressed, river cannot flow further`, false, "error");
|
||||||
|
|
||||||
const [tx, ty] = cells.p[min];
|
|
||||||
|
|
||||||
// pour to water body
|
// pour to water body
|
||||||
if (h[min] < 20) {
|
if (h[min] < 20) {
|
||||||
riverCells.push(min);
|
riverCells.push(min);
|
||||||
|
|
@ -615,12 +613,11 @@ function addRiverOnClick() {
|
||||||
}
|
}
|
||||||
|
|
||||||
const river = rivers.find(r => r.i === riverId);
|
const river = rivers.find(r => r.i === riverId);
|
||||||
const sourceWidth = 0.1;
|
|
||||||
const widthFactor = river?.widthFactor || (!parent || parent === r ? 1.2 : 1);
|
const widthFactor = river?.widthFactor || (!parent || parent === r ? 1.2 : 1);
|
||||||
|
|
||||||
const riverMeandered = Rivers.addMeandering(riverCells, sourceWidth * 10, 0.5);
|
const riverMeandered = Rivers.addMeandering(riverCells, 1);
|
||||||
lineGen.curve(d3.curveCatmullRom.alpha(0.1));
|
lineGen.curve(d3.curveCatmullRom.alpha(0.1));
|
||||||
const [path, length, offset] = Rivers.getRiverPath(riverMeandered, widthFactor, sourceWidth);
|
const [path, length, offset] = Rivers.getRiverPath(riverMeandered, widthFactor);
|
||||||
viewbox
|
viewbox
|
||||||
.select("#rivers")
|
.select("#rivers")
|
||||||
.append("path")
|
.append("path")
|
||||||
|
|
@ -629,9 +626,9 @@ function addRiverOnClick() {
|
||||||
|
|
||||||
// add new river to data or change extended river attributes
|
// add new river to data or change extended river attributes
|
||||||
const source = riverCells[0];
|
const source = riverCells[0];
|
||||||
const mouth = last(riverCells);
|
const mouth = riverCells[riverCells.length - 2];
|
||||||
const discharge = cells.fl[mouth]; // in m3/s
|
const discharge = cells.fl[mouth]; // m3 in second
|
||||||
const width = rn(offset ** 2, 2); // mounth width in km
|
const width = rn((offset / 1.4) ** 2, 2); // mounth width in km
|
||||||
|
|
||||||
if (river) {
|
if (river) {
|
||||||
river.source = source;
|
river.source = source;
|
||||||
|
|
|
||||||
|
|
@ -236,6 +236,10 @@ function P(probability) {
|
||||||
return Math.random() < probability;
|
return Math.random() < probability;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function each(n) {
|
||||||
|
return i => i % n === 0;
|
||||||
|
}
|
||||||
|
|
||||||
// random number (normal or gaussian distribution)
|
// random number (normal or gaussian distribution)
|
||||||
function gauss(expected = 100, deviation = 30, min = 0, max = 300, round = 0) {
|
function gauss(expected = 100, deviation = 30, min = 0, max = 300, round = 0) {
|
||||||
return rn(Math.max(Math.min(d3.randomNormal(expected, deviation)(), max), min), round);
|
return rn(Math.max(Math.min(d3.randomNormal(expected, deviation)(), max), min), round);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue