diff --git a/index.html b/index.html
index eb363635..907d690a 100644
--- a/index.html
+++ b/index.html
@@ -1358,9 +1358,9 @@
-
diff --git a/main.js b/main.js
index b7a6f37a..347670c4 100644
--- a/main.js
+++ b/main.js
@@ -598,6 +598,7 @@ function generate() {
HeightmapGenerator.generate();
markFeatures();
markupGridOcean();
+ addLakesInDeepDepressions();
openNearSeaLakes();
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)
function openNearSeaLakes() {
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
TIME && console.time("openLakes");
const LIMIT = 22; // max height that can be breached by water
diff --git a/modules/ocean-layers.js b/modules/ocean-layers.js
index 2194f7b0..16a8ce88 100644
--- a/modules/ocean-layers.js
+++ b/modules/ocean-layers.js
@@ -1,8 +1,7 @@
(function (global, factory) {
- typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
- typeof define === 'function' && define.amd ? define(factory) :
- (global.OceanLayers = factory());
-}(this, (function () { 'use strict';
+ typeof exports === "object" && typeof module !== "undefined" ? (module.exports = factory()) : typeof define === "function" && define.amd ? define(factory) : (global.OceanLayers = factory());
+})(this, function () {
+ "use strict";
let cells, vertices, pointsN, used;
@@ -12,11 +11,11 @@
TIME && console.time("drawOceanLayers");
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 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
for (const i of cells.i) {
@@ -29,10 +28,13 @@
const chain = connectVertices(start, t); // vertices chain to form a path
if (chain.length < 4) continue;
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;
- const points = clipPoly(relaxed.map(v => vertices.p[v]), 1);
- chains.push([t, points]);
+ const points = clipPoly(
+ relaxed.map(v => vertices.p[v]),
+ 1
+ );
+ chains.push([t, points]);
}
for (const t of limits) {
@@ -48,14 +50,18 @@
}
TIME && console.timeEnd("drawOceanLayers");
- }
+ };
function randomizeOutline() {
const limits = [];
- let odd = .2
+ let odd = 0.2;
for (let l = -9; l < 0; l++) {
- if (P(odd)) {odd = .2; limits.push(l);}
- else {odd *= 2;}
+ if (P(odd)) {
+ odd = 0.2;
+ limits.push(l);
+ } else {
+ odd *= 2;
+ }
}
return limits;
}
@@ -63,24 +69,26 @@
// connect vertices to chain
function connectVertices(start, t) {
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
chain.push(current); // add current vertex to sequence
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 c0 = !cells.t[c[0]] || cells.t[c[0]] === 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 c0 = !cells.t[c[0]] || cells.t[c[0]] === 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;
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[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
return chain;
}
return OceanLayers;
-
-})));
\ No newline at end of file
+});
diff --git a/modules/river-generator.js b/modules/river-generator.js
index 8dcda0f6..72dfac1e 100644
--- a/modules/river-generator.js
+++ b/modules/river-generator.js
@@ -3,7 +3,7 @@
})(this, function () {
"use strict";
- const generate = function (changeHeights = true) {
+ const generate = function (allowErosion = true) {
TIME && console.time("generateRivers");
Math.random = aleaPRNG(seed);
const cells = pack.cells,
@@ -23,7 +23,7 @@
defineRivers();
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");
diff --git a/modules/ui/heightmap-editor.js b/modules/ui/heightmap-editor.js
index 0b7ef617..935660ca 100644
--- a/modules/ui/heightmap-editor.js
+++ b/modules/ui/heightmap-editor.js
@@ -77,7 +77,7 @@ function editHeightmap() {
convertImage.style.display = type === "erase" ? "inline-block" : "none";
// 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
if (!sessionStorage.getItem("noExitButtonAnimation")) {
@@ -182,19 +182,22 @@ function editHeightmap() {
INFO && console.group("Edit Heightmap");
TIME && console.time("regenerateErasedData");
- const change = changeHeights.checked;
+ const erosionAllowed = allowErosion.checked;
markFeatures();
markupGridOcean();
- if (change) openNearSeaLakes();
+ if (erosionAllowed) {
+ addLakesInDeepDepressions();
+ openNearSeaLakes();
+ }
OceanLayers();
calculateTemperatures();
generatePrecipitation();
reGraph();
drawCoastline();
- Rivers.generate(change);
+ Rivers.generate(erosionAllowed);
- if (!change) {
+ if (!erosionAllowed) {
for (const i of pack.cells.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];
@@ -236,6 +239,7 @@ function editHeightmap() {
function restoreRiskedData() {
INFO && console.group("Edit Heightmap");
TIME && console.time("restoreRiskedData");
+ const erosionAllowed = allowErosion.checked;
// assign pack data to grid cells
const l = grid.cells.i.length;
@@ -250,7 +254,7 @@ function editHeightmap() {
const culture = 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 r = new Uint16Array(l);
const conf = new Uint8Array(l);
@@ -268,7 +272,7 @@ function editHeightmap() {
burg[g] = pack.cells.burg[i];
religion[g] = pack.cells.religion[i];
- if (!changeHeights.checked) {
+ if (!erosionAllowed) {
fl[g] = pack.cells.fl[i];
r[g] = pack.cells.r[i];
conf[g] = pack.cells.conf[i];
@@ -301,13 +305,14 @@ function editHeightmap() {
markFeatures();
markupGridOcean();
+ if (erosionAllowed) addLakesInDeepDepressions();
OceanLayers();
calculateTemperatures();
generatePrecipitation();
reGraph();
drawCoastline();
- if (changeHeights.checked) Rivers.generate(changeHeights.checked);
+ if (erosionAllowed) Rivers.generate(true);
// assign saved pack data from grid back to pack
const n = pack.cells.i.length;
@@ -322,7 +327,7 @@ function editHeightmap() {
pack.cells.religion = new Uint16Array(n);
pack.cells.biome = new Uint8Array(n);
- if (!changeHeights.checked) {
+ if (!erosionAllowed) {
pack.cells.r = new Uint16Array(n);
pack.cells.conf = new Uint8Array(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]);
// rivers data
- if (!changeHeights.checked) {
+ if (!erosionAllowed) {
pack.cells.r[i] = r[g];
pack.cells.conf[i] = conf[g];
pack.cells.fl[i] = fl[g];
@@ -400,7 +405,7 @@ function editHeightmap() {
drawStates();
drawBorders();
- if (changeHeights.checked) {
+ if (erosion) {
Rivers.specify();
Lakes.generateName();
}