mirror of
https://github.com/Azgaar/Fantasy-Map-Generator.git
synced 2025-12-17 09:41:24 +01:00
addLakesInDeepDepressions
This commit is contained in:
parent
6cca0b7f38
commit
c8c1c24909
5 changed files with 113 additions and 39 deletions
|
|
@ -1358,9 +1358,9 @@
|
||||||
<input id="renderOcean" class="checkbox" type="checkbox">
|
<input id="renderOcean" class="checkbox" type="checkbox">
|
||||||
<label for="renderOcean" class="checkbox-label">Render ocean cells</label>
|
<label for="renderOcean" class="checkbox-label">Render ocean cells</label>
|
||||||
</div>
|
</div>
|
||||||
<div id="changeHeightsBox" data-tip="Regenerate rivers and allow water flow to slightly change heights">
|
<div id="allowErosionBox" data-tip="Regenerate rivers and allow water flow to change heights and form lakes">
|
||||||
<input id="changeHeights" class="checkbox" type="checkbox" checked>
|
<input id="allowErosion" class="checkbox" type="checkbox" checked>
|
||||||
<label for="changeHeights" class="checkbox-label">Allow water erosion</label>
|
<label for="allowErosion" class="checkbox-label">Allow water erosion</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
65
main.js
65
main.js
|
|
@ -598,6 +598,7 @@ function generate() {
|
||||||
HeightmapGenerator.generate();
|
HeightmapGenerator.generate();
|
||||||
markFeatures();
|
markFeatures();
|
||||||
markupGridOcean();
|
markupGridOcean();
|
||||||
|
addLakesInDeepDepressions();
|
||||||
openNearSeaLakes();
|
openNearSeaLakes();
|
||||||
|
|
||||||
OceanLayers();
|
OceanLayers();
|
||||||
|
|
@ -770,11 +771,71 @@ function markup(cells, start, increment, limit) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function addLakesInDeepDepressions() {
|
||||||
|
console.time("addLakesInDeepDepressions");
|
||||||
|
const {cells, features, points} = grid;
|
||||||
|
const {c, h, b} = cells;
|
||||||
|
const ELEVATION_LIMIT = 10;
|
||||||
|
|
||||||
|
for (const i of cells.i) {
|
||||||
|
if (b[i] || h[i] < 20) continue;
|
||||||
|
|
||||||
|
const minHeight = d3.min(c[i].map(c => h[c]));
|
||||||
|
if (h[i] > minHeight) continue;
|
||||||
|
|
||||||
|
let deep = true;
|
||||||
|
const treshold = h[i] + ELEVATION_LIMIT;
|
||||||
|
const queue = [i];
|
||||||
|
const checked = [];
|
||||||
|
checked[i] = true;
|
||||||
|
|
||||||
|
// check if elevated cell can potentially pour to water
|
||||||
|
while (deep && queue.length) {
|
||||||
|
const q = queue.pop();
|
||||||
|
|
||||||
|
for (const n of c[q]) {
|
||||||
|
if (checked[n]) continue;
|
||||||
|
if (h[n] < 20) {
|
||||||
|
deep = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (h[n] >= treshold) continue;
|
||||||
|
|
||||||
|
checked[n] = true;
|
||||||
|
queue.push(n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if not, add a lake
|
||||||
|
if (deep) {
|
||||||
|
debug.append("circle").attr("cx", points[i][0]).attr("cy", points[i][1]).attr("r", 1).attr("fill", "red");
|
||||||
|
const lakeCells = [i].concat(c[i].filter(n => h[n] === h[i]));
|
||||||
|
addLake(lakeCells);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function addLake(lakeCells) {
|
||||||
|
const f = features.length;
|
||||||
|
|
||||||
|
lakeCells.forEach(i => {
|
||||||
|
cells.h[i] = 19;
|
||||||
|
cells.t[i] = -1;
|
||||||
|
cells.f[i] = f;
|
||||||
|
c[i].forEach(n => !lakeCells.includes(n) && (cells.t[c] = 1));
|
||||||
|
});
|
||||||
|
|
||||||
|
features.push({i: f, land: false, border: false, type: "lake"});
|
||||||
|
}
|
||||||
|
|
||||||
|
console.timeEnd("addLakesInDeepDepressions");
|
||||||
|
}
|
||||||
|
|
||||||
// near sea lakes usually get a lot of water inflow, most of them should brake treshold and flow out to sea (see Ancylus Lake)
|
// near sea lakes usually get a lot of water inflow, most of them should brake treshold and flow out to sea (see Ancylus Lake)
|
||||||
function openNearSeaLakes() {
|
function openNearSeaLakes() {
|
||||||
if (templateInput.value === "Atoll") return; // no need for Atolls
|
if (templateInput.value === "Atoll") return; // no need for Atolls
|
||||||
const cells = grid.cells,
|
|
||||||
features = grid.features;
|
const cells = grid.cells;
|
||||||
|
const features = grid.features;
|
||||||
if (!features.find(f => f.type === "lake")) return; // no lakes
|
if (!features.find(f => f.type === "lake")) return; // no lakes
|
||||||
TIME && console.time("openLakes");
|
TIME && console.time("openLakes");
|
||||||
const LIMIT = 22; // max height that can be breached by water
|
const LIMIT = 22; // max height that can be breached by water
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
(function (global, factory) {
|
(function (global, factory) {
|
||||||
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
|
typeof exports === "object" && typeof module !== "undefined" ? (module.exports = factory()) : typeof define === "function" && define.amd ? define(factory) : (global.OceanLayers = factory());
|
||||||
typeof define === 'function' && define.amd ? define(factory) :
|
})(this, function () {
|
||||||
(global.OceanLayers = factory());
|
"use strict";
|
||||||
}(this, (function () { 'use strict';
|
|
||||||
|
|
||||||
let cells, vertices, pointsN, used;
|
let cells, vertices, pointsN, used;
|
||||||
|
|
||||||
|
|
@ -12,11 +11,11 @@
|
||||||
TIME && console.time("drawOceanLayers");
|
TIME && console.time("drawOceanLayers");
|
||||||
|
|
||||||
lineGen.curve(d3.curveBasisClosed);
|
lineGen.curve(d3.curveBasisClosed);
|
||||||
cells = grid.cells, pointsN = grid.cells.i.length, vertices = grid.vertices;
|
(cells = grid.cells), (pointsN = grid.cells.i.length), (vertices = grid.vertices);
|
||||||
const limits = outline === "random" ? randomizeOutline() : outline.split(",").map(s => +s);
|
const limits = outline === "random" ? randomizeOutline() : outline.split(",").map(s => +s);
|
||||||
|
|
||||||
const chains = [];
|
const chains = [];
|
||||||
const opacity = rn(.4 / limits.length, 2);
|
const opacity = rn(0.4 / limits.length, 2);
|
||||||
used = new Uint8Array(pointsN); // to detect already passed cells
|
used = new Uint8Array(pointsN); // to detect already passed cells
|
||||||
|
|
||||||
for (const i of cells.i) {
|
for (const i of cells.i) {
|
||||||
|
|
@ -29,9 +28,12 @@
|
||||||
const chain = connectVertices(start, t); // vertices chain to form a path
|
const chain = connectVertices(start, t); // vertices chain to form a path
|
||||||
if (chain.length < 4) continue;
|
if (chain.length < 4) continue;
|
||||||
const relax = 1 + t * -2; // select only n-th point
|
const relax = 1 + t * -2; // select only n-th point
|
||||||
const relaxed = chain.filter((v, i) => !(i%relax) || vertices.c[v].some(c => c >= pointsN));
|
const relaxed = chain.filter((v, i) => !(i % relax) || vertices.c[v].some(c => c >= pointsN));
|
||||||
if (relaxed.length < 4) continue;
|
if (relaxed.length < 4) continue;
|
||||||
const points = clipPoly(relaxed.map(v => vertices.p[v]), 1);
|
const points = clipPoly(
|
||||||
|
relaxed.map(v => vertices.p[v]),
|
||||||
|
1
|
||||||
|
);
|
||||||
chains.push([t, points]);
|
chains.push([t, points]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -48,14 +50,18 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
TIME && console.timeEnd("drawOceanLayers");
|
TIME && console.timeEnd("drawOceanLayers");
|
||||||
}
|
};
|
||||||
|
|
||||||
function randomizeOutline() {
|
function randomizeOutline() {
|
||||||
const limits = [];
|
const limits = [];
|
||||||
let odd = .2
|
let odd = 0.2;
|
||||||
for (let l = -9; l < 0; l++) {
|
for (let l = -9; l < 0; l++) {
|
||||||
if (P(odd)) {odd = .2; limits.push(l);}
|
if (P(odd)) {
|
||||||
else {odd *= 2;}
|
odd = 0.2;
|
||||||
|
limits.push(l);
|
||||||
|
} else {
|
||||||
|
odd *= 2;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return limits;
|
return limits;
|
||||||
}
|
}
|
||||||
|
|
@ -63,24 +69,26 @@
|
||||||
// connect vertices to chain
|
// connect vertices to chain
|
||||||
function connectVertices(start, t) {
|
function connectVertices(start, t) {
|
||||||
const chain = []; // vertices chain to form a path
|
const chain = []; // vertices chain to form a path
|
||||||
for (let i=0, current = start; i === 0 || current !== start && i < 10000; i++) {
|
for (let i = 0, current = start; i === 0 || (current !== start && i < 10000); i++) {
|
||||||
const prev = chain[chain.length - 1]; // previous vertex in chain
|
const prev = chain[chain.length - 1]; // previous vertex in chain
|
||||||
chain.push(current); // add current vertex to sequence
|
chain.push(current); // add current vertex to sequence
|
||||||
const c = vertices.c[current]; // cells adjacent to vertex
|
const c = vertices.c[current]; // cells adjacent to vertex
|
||||||
c.filter(c => cells.t[c] === t).forEach(c => used[c] = 1);
|
c.filter(c => cells.t[c] === t).forEach(c => (used[c] = 1));
|
||||||
const v = vertices.v[current]; // neighboring vertices
|
const v = vertices.v[current]; // neighboring vertices
|
||||||
const c0 = !cells.t[c[0]] || cells.t[c[0]] === t-1;
|
const c0 = !cells.t[c[0]] || cells.t[c[0]] === t - 1;
|
||||||
const c1 = !cells.t[c[1]] || cells.t[c[1]] === t-1;
|
const c1 = !cells.t[c[1]] || cells.t[c[1]] === t - 1;
|
||||||
const c2 = !cells.t[c[2]] || cells.t[c[2]] === t-1;
|
const c2 = !cells.t[c[2]] || cells.t[c[2]] === t - 1;
|
||||||
if (v[0] !== undefined && v[0] !== prev && c0 !== c1) current = v[0];
|
if (v[0] !== undefined && v[0] !== prev && c0 !== c1) current = v[0];
|
||||||
else if (v[1] !== undefined && v[1] !== prev && c1 !== c2) current = v[1];
|
else if (v[1] !== undefined && v[1] !== prev && c1 !== c2) current = v[1];
|
||||||
else if (v[2] !== undefined && v[2] !== prev && c0 !== c2) current = v[2];
|
else if (v[2] !== undefined && v[2] !== prev && c0 !== c2) current = v[2];
|
||||||
if (current === chain[chain.length - 1]) {ERROR && console.error("Next vertex is not found"); break;}
|
if (current === chain[chain.length - 1]) {
|
||||||
|
ERROR && console.error("Next vertex is not found");
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
chain.push(chain[0]); // push first vertex as the last one
|
chain.push(chain[0]); // push first vertex as the last one
|
||||||
return chain;
|
return chain;
|
||||||
}
|
}
|
||||||
|
|
||||||
return OceanLayers;
|
return OceanLayers;
|
||||||
|
});
|
||||||
})));
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
})(this, function () {
|
})(this, function () {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const generate = function (changeHeights = true) {
|
const generate = function (allowErosion = true) {
|
||||||
TIME && console.time("generateRivers");
|
TIME && console.time("generateRivers");
|
||||||
Math.random = aleaPRNG(seed);
|
Math.random = aleaPRNG(seed);
|
||||||
const cells = pack.cells,
|
const cells = pack.cells,
|
||||||
|
|
@ -23,7 +23,7 @@
|
||||||
defineRivers();
|
defineRivers();
|
||||||
Lakes.cleanupLakeData();
|
Lakes.cleanupLakeData();
|
||||||
|
|
||||||
if (changeHeights) cells.h = Uint8Array.from(h); // apply changed heights as basic one
|
if (allowErosion) cells.h = Uint8Array.from(h); // apply changed heights as basic one
|
||||||
|
|
||||||
TIME && console.timeEnd("generateRivers");
|
TIME && console.timeEnd("generateRivers");
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -77,7 +77,7 @@ function editHeightmap() {
|
||||||
convertImage.style.display = type === "erase" ? "inline-block" : "none";
|
convertImage.style.display = type === "erase" ? "inline-block" : "none";
|
||||||
|
|
||||||
// hide erosion checkbox if mode is Keep
|
// hide erosion checkbox if mode is Keep
|
||||||
changeHeightsBox.style.display = type === "keep" ? "none" : "inline-block";
|
allowErosionBox.style.display = type === "keep" ? "none" : "inline-block";
|
||||||
|
|
||||||
// show finalize button
|
// show finalize button
|
||||||
if (!sessionStorage.getItem("noExitButtonAnimation")) {
|
if (!sessionStorage.getItem("noExitButtonAnimation")) {
|
||||||
|
|
@ -182,19 +182,22 @@ function editHeightmap() {
|
||||||
INFO && console.group("Edit Heightmap");
|
INFO && console.group("Edit Heightmap");
|
||||||
TIME && console.time("regenerateErasedData");
|
TIME && console.time("regenerateErasedData");
|
||||||
|
|
||||||
const change = changeHeights.checked;
|
const erosionAllowed = allowErosion.checked;
|
||||||
markFeatures();
|
markFeatures();
|
||||||
markupGridOcean();
|
markupGridOcean();
|
||||||
if (change) openNearSeaLakes();
|
if (erosionAllowed) {
|
||||||
|
addLakesInDeepDepressions();
|
||||||
|
openNearSeaLakes();
|
||||||
|
}
|
||||||
OceanLayers();
|
OceanLayers();
|
||||||
calculateTemperatures();
|
calculateTemperatures();
|
||||||
generatePrecipitation();
|
generatePrecipitation();
|
||||||
reGraph();
|
reGraph();
|
||||||
drawCoastline();
|
drawCoastline();
|
||||||
|
|
||||||
Rivers.generate(change);
|
Rivers.generate(erosionAllowed);
|
||||||
|
|
||||||
if (!change) {
|
if (!erosionAllowed) {
|
||||||
for (const i of pack.cells.i) {
|
for (const i of pack.cells.i) {
|
||||||
const g = pack.cells.g[i];
|
const g = pack.cells.g[i];
|
||||||
if (pack.cells.h[i] !== grid.cells.h[g] && pack.cells.h[i] >= 20 === grid.cells.h[g] >= 20) pack.cells.h[i] = grid.cells.h[g];
|
if (pack.cells.h[i] !== grid.cells.h[g] && pack.cells.h[i] >= 20 === grid.cells.h[g] >= 20) pack.cells.h[i] = grid.cells.h[g];
|
||||||
|
|
@ -236,6 +239,7 @@ function editHeightmap() {
|
||||||
function restoreRiskedData() {
|
function restoreRiskedData() {
|
||||||
INFO && console.group("Edit Heightmap");
|
INFO && console.group("Edit Heightmap");
|
||||||
TIME && console.time("restoreRiskedData");
|
TIME && console.time("restoreRiskedData");
|
||||||
|
const erosionAllowed = allowErosion.checked;
|
||||||
|
|
||||||
// assign pack data to grid cells
|
// assign pack data to grid cells
|
||||||
const l = grid.cells.i.length;
|
const l = grid.cells.i.length;
|
||||||
|
|
@ -250,7 +254,7 @@ function editHeightmap() {
|
||||||
const culture = new Uint16Array(l);
|
const culture = new Uint16Array(l);
|
||||||
const religion = new Uint16Array(l);
|
const religion = new Uint16Array(l);
|
||||||
|
|
||||||
// rivers data, stored only if changeHeights is unchecked
|
// rivers data, stored only if allowErosion is unchecked
|
||||||
const fl = new Uint16Array(l);
|
const fl = new Uint16Array(l);
|
||||||
const r = new Uint16Array(l);
|
const r = new Uint16Array(l);
|
||||||
const conf = new Uint8Array(l);
|
const conf = new Uint8Array(l);
|
||||||
|
|
@ -268,7 +272,7 @@ function editHeightmap() {
|
||||||
burg[g] = pack.cells.burg[i];
|
burg[g] = pack.cells.burg[i];
|
||||||
religion[g] = pack.cells.religion[i];
|
religion[g] = pack.cells.religion[i];
|
||||||
|
|
||||||
if (!changeHeights.checked) {
|
if (!erosionAllowed) {
|
||||||
fl[g] = pack.cells.fl[i];
|
fl[g] = pack.cells.fl[i];
|
||||||
r[g] = pack.cells.r[i];
|
r[g] = pack.cells.r[i];
|
||||||
conf[g] = pack.cells.conf[i];
|
conf[g] = pack.cells.conf[i];
|
||||||
|
|
@ -301,13 +305,14 @@ function editHeightmap() {
|
||||||
|
|
||||||
markFeatures();
|
markFeatures();
|
||||||
markupGridOcean();
|
markupGridOcean();
|
||||||
|
if (erosionAllowed) addLakesInDeepDepressions();
|
||||||
OceanLayers();
|
OceanLayers();
|
||||||
calculateTemperatures();
|
calculateTemperatures();
|
||||||
generatePrecipitation();
|
generatePrecipitation();
|
||||||
reGraph();
|
reGraph();
|
||||||
drawCoastline();
|
drawCoastline();
|
||||||
|
|
||||||
if (changeHeights.checked) Rivers.generate(changeHeights.checked);
|
if (erosionAllowed) Rivers.generate(true);
|
||||||
|
|
||||||
// assign saved pack data from grid back to pack
|
// assign saved pack data from grid back to pack
|
||||||
const n = pack.cells.i.length;
|
const n = pack.cells.i.length;
|
||||||
|
|
@ -322,7 +327,7 @@ function editHeightmap() {
|
||||||
pack.cells.religion = new Uint16Array(n);
|
pack.cells.religion = new Uint16Array(n);
|
||||||
pack.cells.biome = new Uint8Array(n);
|
pack.cells.biome = new Uint8Array(n);
|
||||||
|
|
||||||
if (!changeHeights.checked) {
|
if (!erosionAllowed) {
|
||||||
pack.cells.r = new Uint16Array(n);
|
pack.cells.r = new Uint16Array(n);
|
||||||
pack.cells.conf = new Uint8Array(n);
|
pack.cells.conf = new Uint8Array(n);
|
||||||
pack.cells.fl = new Uint16Array(n);
|
pack.cells.fl = new Uint16Array(n);
|
||||||
|
|
@ -336,7 +341,7 @@ function editHeightmap() {
|
||||||
pack.cells.biome[i] = land && biome[g] ? biome[g] : getBiomeId(grid.cells.prec[g], pack.cells.h[i]);
|
pack.cells.biome[i] = land && biome[g] ? biome[g] : getBiomeId(grid.cells.prec[g], pack.cells.h[i]);
|
||||||
|
|
||||||
// rivers data
|
// rivers data
|
||||||
if (!changeHeights.checked) {
|
if (!erosionAllowed) {
|
||||||
pack.cells.r[i] = r[g];
|
pack.cells.r[i] = r[g];
|
||||||
pack.cells.conf[i] = conf[g];
|
pack.cells.conf[i] = conf[g];
|
||||||
pack.cells.fl[i] = fl[g];
|
pack.cells.fl[i] = fl[g];
|
||||||
|
|
@ -400,7 +405,7 @@ function editHeightmap() {
|
||||||
drawStates();
|
drawStates();
|
||||||
drawBorders();
|
drawBorders();
|
||||||
|
|
||||||
if (changeHeights.checked) {
|
if (erosion) {
|
||||||
Rivers.specify();
|
Rivers.specify();
|
||||||
Lakes.generateName();
|
Lakes.generateName();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue