diff --git a/index.html b/index.html
index b8d31638..b5cc670a 100644
--- a/index.html
+++ b/index.html
@@ -7642,7 +7642,6 @@
-
diff --git a/src/dialogs/dialogs/heightmap-editor.js b/src/dialogs/dialogs/heightmap-editor.js
index 5a9d9f71..214189b4 100644
--- a/src/dialogs/dialogs/heightmap-editor.js
+++ b/src/dialogs/dialogs/heightmap-editor.js
@@ -215,10 +215,10 @@ export function open(options) {
const erosionAllowed = allowErosion.checked;
markupGridFeatures();
- if (erosionAllowed) {
- Lakes.addLakesInDeepDepressions(grid);
- Lakes.openNearSeaLakes(grid);
- }
+ // if (erosionAllowed) {
+ // Lakes.addLakesInDeepDepressions(grid);
+ // Lakes.openNearSeaLakes(grid);
+ // }
OceanLayers(grid);
calculateTemperatures(grid);
generatePrecipitation(grid);
diff --git a/src/modules/heightmap-generator.js b/src/modules/heightmap-generator.js
index ec2235c2..f792f8c7 100644
--- a/src/modules/heightmap-generator.js
+++ b/src/modules/heightmap-generator.js
@@ -9,6 +9,7 @@ import {byId} from "utils/shorthands";
import {ERROR} from "../config/logging";
import {lim, minmax} from "../utils/numberUtils";
import {aleaPRNG} from "scripts/aleaPRNG";
+import {addLakesInDeepDepressions} from "scripts/generation/grid/lakes";
window.HeightmapGenerator = (function () {
let grid = null;
@@ -77,7 +78,10 @@ window.HeightmapGenerator = (function () {
Math.random = aleaPRNG(seed);
const isTemplate = id in heightmapTemplates;
- const heights = isTemplate ? fromTemplate(graph, id) : await fromPrecreated(graph, id);
+ const rawHeights = isTemplate ? fromTemplate(graph, id) : await fromPrecreated(graph, id);
+
+ const heights = addLakesInDeepDepressions(rawHeights, graph.cells.c, graph.cells.v, graph.vertices, graph.cells.i);
+
TIME && console.timeEnd("defineHeightmap");
clearData();
diff --git a/src/modules/lakes.ts b/src/modules/lakes.ts
deleted file mode 100644
index 55ccb5ff..00000000
--- a/src/modules/lakes.ts
+++ /dev/null
@@ -1,126 +0,0 @@
-// @ts-nocheck
-import * as d3 from "d3";
-
-import {TIME} from "config/logging";
-import {aleaPRNG} from "scripts/aleaPRNG";
-import {getInputNumber, getInputValue} from "utils/nodeUtils";
-import {DISTANCE_FIELD, MIN_LAND_HEIGHT} from "config/generation";
-import {byId} from "utils/shorthands";
-
-window.Lakes = (function () {
- const {LAND_COAST, WATER_COAST} = DISTANCE_FIELD;
-
- function addLakesInDeepDepressions(grid: IGraph & Partial) {
- const ELEVATION_LIMIT = getInputNumber("lakeElevationLimitOutput");
- if (ELEVATION_LIMIT === 80) return;
-
- TIME && console.time("addLakesInDeepDepressions");
- const {cells, features} = grid;
- if (!features) throw new Error("addLakesInDeepDepressions: features are not defined");
- const {c, h, b} = cells;
-
- for (const i of cells.i) {
- if (b[i] || h[i] < MIN_LAND_HEIGHT) continue;
-
- const minHeight = d3.min(c[i].map(c => h[c])) || 0;
- if (h[i] > minHeight) continue;
-
- let deep = true;
- const threshold = 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] >= threshold) continue;
- if (h[n] < MIN_LAND_HEIGHT) {
- deep = false;
- break;
- }
-
- checked[n] = true;
- queue.push(n);
- }
- }
-
- // if not, add a lake
- if (deep) {
- const lakeCells = [i].concat(c[i].filter(n => h[n] === h[i]));
- addLake(lakeCells);
- }
- }
-
- function addLake(lakeCells: number[]) {
- const featureId = features!.length;
-
- for (const lakeCellId of lakeCells) {
- cells.h[lakeCellId] = MIN_LAND_HEIGHT - 1;
- cells.t[lakeCellId] = WATER_COAST;
- cells.f[lakeCellId] = featureId;
-
- for (const neibCellId of c[lakeCellId]) {
- if (!lakeCells.includes(neibCellId)) cells.t[neibCellId] = LAND_COAST;
- }
- }
-
- features!.push({i: featureId, land: false, border: false, type: "lake"});
- }
-
- TIME && console.timeEnd("addLakesInDeepDepressions");
- }
-
- // near sea lakes usually get a lot of water inflow, most of them should brake threshold and flow out to sea (see Ancylus Lake)
- function openNearSeaLakes(grid: IGraph & Partial) {
- if (getInputValue("templateInput") === "Atoll") return; // no need for Atolls
-
- const {cells, features} = grid;
- if (!features?.find(f => f && f.type === "lake")) return; // no lakes
-
- TIME && console.time("openLakes");
- const LIMIT = 22; // max height that can be breached by water
-
- const isLake = (featureId: number) => featureId && (features[featureId] as IGridFeature).type === "lake";
- const isOcean = (featureId: number) => featureId && (features[featureId] as IGridFeature).type === "ocean";
-
- for (const cellId of cells.i) {
- const featureId = cells.f[cellId];
- if (!isLake(featureId)) continue; // not a lake cell
-
- check_neighbours: for (const neibCellId of cells.c[cellId]) {
- // water cannot brake the barrier
- if (cells.t[neibCellId] !== WATER_COAST || cells.h[neibCellId] > LIMIT) continue;
-
- for (const neibOfNeibCellId of cells.c[neibCellId]) {
- const neibOfNeibFeatureId = cells.f[neibOfNeibCellId];
- if (!isOcean(neibOfNeibFeatureId)) continue; // not an ocean
- removeLake(neibCellId, featureId, neibOfNeibFeatureId);
- break check_neighbours;
- }
- }
- }
-
- function removeLake(barrierCellId: number, lakeFeatureId: number, oceanFeatureId: number) {
- cells.h[barrierCellId] = MIN_LAND_HEIGHT - 1;
- cells.t[barrierCellId] = WATER_COAST;
- cells.f[barrierCellId] = oceanFeatureId;
-
- for (const neibCellId of cells.c[barrierCellId]) {
- if (cells.h[neibCellId] >= MIN_LAND_HEIGHT) cells.t[neibCellId] = LAND_COAST;
- }
-
- if (features && lakeFeatureId) {
- // mark former lake as ocean
- (features[lakeFeatureId] as IGridFeature).type = "ocean";
- }
- }
-
- TIME && console.timeEnd("openLakes");
- }
-
- return {generateName, getName, addLakesInDeepDepressions, openNearSeaLakes};
-})();
diff --git a/src/modules/submap.js b/src/modules/submap.js
index e81ee0fe..75dc0daf 100644
--- a/src/modules/submap.js
+++ b/src/modules/submap.js
@@ -118,10 +118,10 @@ window.Submap = (function () {
markupGridFeatures();
// Warning: addLakesInDeepDepressions can be very slow!
- if (options.addLakesInDepressions) {
- Lakes.addLakesInDeepDepressions(grid);
- Lakes.openNearSeaLakes(grid);
- }
+ // if (options.addLakesInDepressions) {
+ // Lakes.addLakesInDeepDepressions(grid);
+ // Lakes.openNearSeaLakes(grid);
+ // }
OceanLayers(grid);
diff --git a/src/scripts/generation/generation.ts b/src/scripts/generation/generation.ts
index 838cc44a..b3de2a66 100644
--- a/src/scripts/generation/generation.ts
+++ b/src/scripts/generation/generation.ts
@@ -22,10 +22,10 @@ import {debounce} from "utils/functionUtils";
import {rn} from "utils/numberUtils";
import {generateSeed} from "utils/probabilityUtils";
import {byId} from "utils/shorthands";
-import {createGrid} from "./grid";
+import {createGrid} from "./grid/grid";
import {createPack} from "./pack/pack";
import {getInputValue, setInputValue} from "utils/nodeUtils";
-// import {Ruler} from "modules/measurers";
+import {calculateMapCoordinates} from "modules/coordinates";
const {Zoom, ThreeD} = window;
@@ -50,6 +50,8 @@ async function generate(options?: IGenerationOptions) {
applyMapSize();
randomizeOptions();
+ window.mapCoordinates = calculateMapCoordinates();
+
const newGrid = await createGrid(grid, precreatedGraph);
const newPack = createPack(newGrid);
@@ -60,10 +62,10 @@ async function generate(options?: IGenerationOptions) {
pack = newPack;
// temp rendering for debug
- renderLayer("cells");
+ // renderLayer("cells");
renderLayer("features");
- renderLayer("heightmap");
- renderLayer("rivers", pack);
+ // renderLayer("heightmap");
+ // renderLayer("rivers", pack);
WARN && console.warn(`TOTAL: ${rn((performance.now() - timeStart) / 1000, 2)}s`);
// showStatistics();
diff --git a/src/scripts/generation/graph.ts b/src/scripts/generation/graph.ts
index 2dd72193..a0cfd557 100644
--- a/src/scripts/generation/graph.ts
+++ b/src/scripts/generation/graph.ts
@@ -2,7 +2,6 @@ import Delaunator from "delaunator";
import {Voronoi} from "modules/voronoi";
import {TIME} from "config/logging";
-// @ts-expect-error js module
import {aleaPRNG} from "scripts/aleaPRNG";
import {createTypedArray} from "utils/arrayUtils";
import {rn} from "utils/numberUtils";
diff --git a/src/scripts/generation/grid.ts b/src/scripts/generation/grid/grid.ts
similarity index 52%
rename from src/scripts/generation/grid.ts
rename to src/scripts/generation/grid/grid.ts
index 0beecad0..1c8ba4e4 100644
--- a/src/scripts/generation/grid.ts
+++ b/src/scripts/generation/grid/grid.ts
@@ -1,41 +1,59 @@
-import {calculateTemperatures} from "modules/temperature";
+import {defineMapSize} from "modules/coordinates";
import {generateGrid} from "scripts/generation/graph";
-import {calculateMapCoordinates, defineMapSize} from "modules/coordinates";
-import {markupGridFeatures} from "modules/markup";
-// @ts-expect-error js module
-import {generatePrecipitation} from "modules/precipitation";
-import {byId} from "utils/shorthands";
+import {markupGridFeatures} from "scripts/generation/markup";
import {rn} from "utils/numberUtils";
+import {byId} from "utils/shorthands";
+import {generatePrecipitation} from "./precipitation";
+import {calculateTemperatures} from "./temperature";
-const {Lakes, HeightmapGenerator} = window;
+const {HeightmapGenerator} = window;
-export async function createGrid(globalGrid: IGrid, precreatedGraph?: IGrid): Promise {
- const baseGrid: IGridBase = shouldRegenerateGridPoints(globalGrid)
- ? (precreatedGraph && undressGrid(precreatedGraph)) || generateGrid()
- : undressGrid(globalGrid);
+export async function createGrid(globalGrid: IGrid, precreatedGrid?: IGrid): Promise {
+ const shouldRegenerate = shouldRegenerateGridPoints(globalGrid);
+ const {spacing, cellsDesired, boundary, points, cellsX, cellsY, cells, vertices} = shouldRegenerate
+ ? (precreatedGrid && undressPrecreatedGrid(precreatedGrid)) || generateGrid()
+ : undressPrecreatedGrid(globalGrid);
- const heights: Uint8Array = await HeightmapGenerator.generate(baseGrid);
+ const heights: Uint8Array = await HeightmapGenerator.generate({
+ vertices,
+ points,
+ cells,
+ cellsDesired,
+ spacing,
+ cellsX,
+ cellsY
+ });
if (!heights) throw new Error("Heightmap generation failed");
- const heightsGrid = {...baseGrid, cells: {...baseGrid.cells, h: heights}};
- const {featureIds, distanceField, features} = markupGridFeatures(heightsGrid);
- const markedGrid = {...heightsGrid, features, cells: {...heightsGrid.cells, f: featureIds, t: distanceField}};
+ const {featureIds, distanceField, features} = markupGridFeatures(cells.c, cells.b, heights);
const touchesEdges = features.some(feature => feature && feature.land && feature.border);
defineMapSize(touchesEdges);
- window.mapCoordinates = calculateMapCoordinates();
- Lakes.addLakesInDeepDepressions(markedGrid);
- Lakes.openNearSeaLakes(markedGrid);
+ const temp = calculateTemperatures(heights, cellsX, points);
+ const prec = generatePrecipitation(heights, temp, cellsX, cellsY);
- const temperature = calculateTemperatures(markedGrid);
- const temperatureGrid = {...markedGrid, cells: {...markedGrid.cells, temp: temperature}};
-
- const prec = generatePrecipitation(temperatureGrid);
- return {...temperatureGrid, cells: {...temperatureGrid.cells, prec}};
+ return {
+ cellsDesired,
+ cellsX,
+ cellsY,
+ spacing,
+ boundary,
+ points,
+ vertices,
+ cells: {
+ ...cells,
+ h: heights,
+ f: featureIds,
+ t: distanceField,
+ prec,
+ temp
+ },
+ features
+ };
}
-function undressGrid(extendedGrid: IGrid): IGridBase {
+function undressPrecreatedGrid(extendedGrid: IGrid) {
const {spacing, cellsDesired, boundary, points, cellsX, cellsY, cells, vertices} = extendedGrid;
const {i, b, c, v} = cells;
return {spacing, cellsDesired, boundary, points, cellsX, cellsY, cells: {i, b, c, v}, vertices};
diff --git a/src/scripts/generation/grid/lakes.ts b/src/scripts/generation/grid/lakes.ts
new file mode 100644
index 00000000..49cd0c36
--- /dev/null
+++ b/src/scripts/generation/grid/lakes.ts
@@ -0,0 +1,117 @@
+import {TIME} from "config/logging";
+import {getInputNumber, getInputValue} from "utils/nodeUtils";
+import {DISTANCE_FIELD, MAX_HEIGHT, MIN_LAND_HEIGHT} from "config/generation";
+import {drawPolygon} from "utils/debugUtils";
+
+const {LAND_COAST, WATER_COAST} = DISTANCE_FIELD;
+
+// near sea lakes usually get a lot of water inflow
+// most of them would brake threshold and flow out to sea (see Ancylus Lake)
+// connect these type of lakes to the main water body to improve the heightmap
+export function openNearSeaLakes(grid: IGraph & Partial) {
+ if (getInputValue("templateInput") === "Atoll") return; // no need for Atolls
+
+ const {cells, features} = grid;
+ if (!features?.find(f => f && f.type === "lake")) return; // no lakes
+
+ TIME && console.time("openNearSeaLakes");
+ const LIMIT = 22; // max height that can be breached by water
+
+ const isLake = (featureId: number) => featureId && (features[featureId] as IGridFeature).type === "lake";
+ const isOcean = (featureId: number) => featureId && (features[featureId] as IGridFeature).type === "ocean";
+
+ for (const cellId of cells.i) {
+ const featureId = cells.f[cellId];
+ if (!isLake(featureId)) continue; // not a lake cell
+
+ check_neighbours: for (const neibCellId of cells.c[cellId]) {
+ // water cannot brake the barrier
+ if (cells.t[neibCellId] !== WATER_COAST || cells.h[neibCellId] > LIMIT) continue;
+
+ for (const neibOfNeibCellId of cells.c[neibCellId]) {
+ const neibOfNeibFeatureId = cells.f[neibOfNeibCellId];
+ if (!isOcean(neibOfNeibFeatureId)) continue; // not an ocean
+ removeLake(neibCellId, featureId, neibOfNeibFeatureId);
+ break check_neighbours;
+ }
+ }
+ }
+
+ function removeLake(barrierCellId: number, lakeFeatureId: number, oceanFeatureId: number) {
+ cells.h[barrierCellId] = MIN_LAND_HEIGHT - 1;
+ cells.t[barrierCellId] = WATER_COAST;
+ cells.f[barrierCellId] = oceanFeatureId;
+
+ for (const neibCellId of cells.c[barrierCellId]) {
+ if (cells.h[neibCellId] >= MIN_LAND_HEIGHT) cells.t[neibCellId] = LAND_COAST;
+ }
+
+ if (features && lakeFeatureId) {
+ // mark former lake as ocean
+ (features[lakeFeatureId] as IGridFeature).type = "ocean";
+ }
+ }
+
+ TIME && console.timeEnd("openNearSeaLakes");
+}
+
+// some deeply depressed areas may not be resolved on river generation
+// this areas tend to collect precipitation, so we can add a lake there to help the resolver
+export function addLakesInDeepDepressions(
+ heights: Uint8Array,
+ neighbours: number[][],
+ cellVertices: number[][],
+ vertices: IGraphVertices,
+ indexes: UintArray
+) {
+ const ELEVATION_LIMIT = getInputNumber("lakeElevationLimitOutput");
+ if (ELEVATION_LIMIT === MAX_HEIGHT - MIN_LAND_HEIGHT) return heights; // any depression can be resolved
+
+ TIME && console.time("addLakesInDeepDepressions");
+
+ const landCells = indexes.filter(i => heights[i] >= MIN_LAND_HEIGHT);
+ landCells.sort((a, b) => heights[a] - heights[b]); // lower elevation first
+
+ const currentHeights = new Uint8Array(heights);
+ const checkedCells: Dict = {[landCells[0]]: true};
+
+ for (const cellId of landCells) {
+ if (checkedCells[cellId]) continue;
+
+ const THESHOLD_HEIGHT = currentHeights[cellId] + ELEVATION_LIMIT;
+
+ let inDeepDepression = true;
+
+ const queue = [cellId];
+ const checkedPaths: Dict = {[cellId]: true};
+
+ while (queue.length) {
+ const nextCellId = queue.pop()!;
+
+ if (currentHeights[nextCellId] < MIN_LAND_HEIGHT) {
+ inDeepDepression = false;
+ break;
+ }
+
+ for (const neibCellId of neighbours[nextCellId]) {
+ if (checkedPaths[neibCellId]) continue;
+
+ checkedPaths[neibCellId] = true;
+ checkedCells[neibCellId] = true;
+
+ if (currentHeights[neibCellId] < THESHOLD_HEIGHT) queue.push(neibCellId);
+ }
+ }
+
+ if (inDeepDepression) {
+ currentHeights[cellId] = MIN_LAND_HEIGHT - 1;
+ console.log(`ⓘ Added lake at deep depression. Cell: ${cellId}`);
+
+ const polygon = cellVertices[cellId].map(vertex => vertices.p[vertex]);
+ drawPolygon(polygon, {stroke: "red", strokeWidth: 1, fill: "none"});
+ }
+ }
+
+ TIME && console.timeEnd("addLakesInDeepDepressions");
+ return currentHeights;
+}
diff --git a/src/modules/precipitation.js b/src/scripts/generation/grid/precipitation.ts
similarity index 67%
rename from src/modules/precipitation.js
rename to src/scripts/generation/grid/precipitation.ts
index aea8c8fe..4b9bfda8 100644
--- a/src/modules/precipitation.js
+++ b/src/scripts/generation/grid/precipitation.ts
@@ -1,19 +1,21 @@
+// @ts-nocheck
import * as d3 from "d3";
import {TIME} from "config/logging";
import {minmax} from "utils/numberUtils";
import {rand} from "utils/probabilityUtils";
+import {getInputNumber, getInputValue} from "utils/nodeUtils";
+import {byId} from "utils/shorthands";
// simplest precipitation model
-export function generatePrecipitation(grid) {
+export function generatePrecipitation(heights: Uint8Array, temperatures: Int8Array, cellsX: number, cellsY: number) {
TIME && console.time("generatePrecipitation");
prec.selectAll("*").remove();
- const {cells, cellsX, cellsY} = grid;
- const precipitation = new Uint8Array(cells.i.length); // precipitation array
+ const precipitation = new Uint8Array(heights.length); // precipitation array
- const cellsNumberModifier = (pointsInput.dataset.cells / 10000) ** 0.25;
- const precInputModifier = precInput.value / 100;
+ const cellsNumberModifier = (byId("pointsInput").dataset.cells / 10000) ** 0.25;
+ const precInputModifier = getInputNumber("precInput") / 100;
const modifier = cellsNumberModifier * precInputModifier;
const westerly = [];
@@ -34,7 +36,7 @@ export function generatePrecipitation(grid) {
const MAX_PASSABLE_ELEVATION = 85;
// define wind directions based on cells latitude and prevailing winds there
- d3.range(0, cells.i.length, cellsX).forEach(function (c, i) {
+ d3.range(0, heights.length, cellsX).forEach(function (c, i) {
const lat = mapCoordinates.latN - (i / cellsY) * mapCoordinates.latT;
const latBand = ((Math.abs(lat) - 1) / 5) | 0;
const latMod = latitudeModifier[latBand];
@@ -63,7 +65,7 @@ export function generatePrecipitation(grid) {
const bandS = ((Math.abs(mapCoordinates.latS) - 1) / 5) | 0;
const latModS = mapCoordinates.latT > 60 ? d3.mean(latitudeModifier) : latitudeModifier[bandS];
const maxPrecS = (southerly / vertT) * 60 * modifier * latModS;
- passWind(d3.range(cells.i.length - cellsX, cells.i.length, 1), maxPrecS, -cellsX, cellsY);
+ passWind(d3.range(heights.length - cellsX, heights.length, 1), maxPrecS, -cellsX, cellsY);
}
function getWindDirections(tier) {
@@ -86,15 +88,15 @@ export function generatePrecipitation(grid) {
first = first[0];
}
- let humidity = maxPrec - cells.h[first]; // initial water amount
+ let humidity = maxPrec - heights[first]; // initial water amount
if (humidity <= 0) continue; // if first cell in row is too elevated consider wind dry
for (let s = 0, current = first; s < steps; s++, current += next) {
- if (cells.temp[current] < -5) continue; // no flux in permafrost
+ if (temperatures[current] < -5) continue; // no flux in permafrost
- if (cells.h[current] < 20) {
+ if (heights[current] < 20) {
// water cell
- if (cells.h[current + next] >= 20) {
+ if (heights[current + next] >= 20) {
precipitation[current + next] += Math.max(humidity / rand(10, 20), 1); // coastal precipitation
} else {
humidity = Math.min(humidity + 5 * modifier, maxPrec); // wind gets more humidity passing water cell
@@ -104,7 +106,7 @@ export function generatePrecipitation(grid) {
}
// land cell
- const isPassable = cells.h[current + next] <= MAX_PASSABLE_ELEVATION;
+ const isPassable = heights[current + next] <= MAX_PASSABLE_ELEVATION;
const cellPrec = isPassable ? getPrecipitation(humidity, current, next) : humidity;
precipitation[current] += cellPrec;
const evaporation = cellPrec > 1.5 ? 1 : 0; // some humidity evaporates back to the atmosphere
@@ -115,53 +117,54 @@ export function generatePrecipitation(grid) {
function getPrecipitation(humidity, i, n) {
const normalLoss = Math.max(humidity / (10 * modifier), 1); // precipitation in normal conditions
- const diff = Math.max(cells.h[i + n] - cells.h[i], 0); // difference in height
- const mod = (cells.h[i + n] / 70) ** 2; // 50 stands for hills, 70 for mountains
+ const diff = Math.max(heights[i + n] - heights[i], 0); // difference in height
+ const mod = (heights[i + n] / 70) ** 2; // 50 stands for hills, 70 for mountains
return minmax(normalLoss + diff * mod, 1, humidity);
}
- void (function drawWindDirection() {
- const wind = prec.append("g").attr("id", "wind");
-
- d3.range(0, 6).forEach(function (t) {
- if (westerly.length > 1) {
- const west = westerly.filter(w => w[2] === t);
- if (west && west.length > 3) {
- const from = west[0][0],
- to = west[west.length - 1][0];
- const y = (grid.points[from][1] + grid.points[to][1]) / 2;
- wind.append("text").attr("x", 20).attr("y", y).text("\u21C9");
- }
- }
- if (easterly.length > 1) {
- const east = easterly.filter(w => w[2] === t);
- if (east && east.length > 3) {
- const from = east[0][0],
- to = east[east.length - 1][0];
- const y = (grid.points[from][1] + grid.points[to][1]) / 2;
- wind
- .append("text")
- .attr("x", graphWidth - 52)
- .attr("y", y)
- .text("\u21C7");
- }
- }
- });
-
- if (northerly)
- wind
- .append("text")
- .attr("x", graphWidth / 2)
- .attr("y", 42)
- .text("\u21CA");
- if (southerly)
- wind
- .append("text")
- .attr("x", graphWidth / 2)
- .attr("y", graphHeight - 20)
- .text("\u21C8");
- })();
-
TIME && console.timeEnd("generatePrecipitation");
return precipitation;
}
+
+// TODO: move to renderer
+function drawWindDirection() {
+ const wind = prec.append("g").attr("id", "wind");
+
+ d3.range(0, 6).forEach(function (t) {
+ if (westerly.length > 1) {
+ const west = westerly.filter(w => w[2] === t);
+ if (west && west.length > 3) {
+ const from = west[0][0];
+ const to = west[west.length - 1][0];
+ const y = (grid.points[from][1] + grid.points[to][1]) / 2;
+ wind.append("text").attr("x", 20).attr("y", y).text("\u21C9");
+ }
+ }
+ if (easterly.length > 1) {
+ const east = easterly.filter(w => w[2] === t);
+ if (east && east.length > 3) {
+ const from = east[0][0];
+ const to = east[east.length - 1][0];
+ const y = (grid.points[from][1] + grid.points[to][1]) / 2;
+ wind
+ .append("text")
+ .attr("x", graphWidth - 52)
+ .attr("y", y)
+ .text("\u21C7");
+ }
+ }
+ });
+
+ if (northerly)
+ wind
+ .append("text")
+ .attr("x", graphWidth / 2)
+ .attr("y", 42)
+ .text("\u21CA");
+ if (southerly)
+ wind
+ .append("text")
+ .attr("x", graphWidth / 2)
+ .attr("y", graphHeight - 20)
+ .text("\u21C8");
+}
diff --git a/src/modules/temperature.ts b/src/scripts/generation/grid/temperature.ts
similarity index 91%
rename from src/modules/temperature.ts
rename to src/scripts/generation/grid/temperature.ts
index 5a5d3c41..b620db24 100644
--- a/src/modules/temperature.ts
+++ b/src/scripts/generation/grid/temperature.ts
@@ -7,12 +7,9 @@ import {MIN_LAND_HEIGHT} from "config/generation";
const interpolate = d3.easePolyInOut.exponent(0.5); // interpolation function
-export function calculateTemperatures(grid: IGridWithHeights) {
+export function calculateTemperatures(heights: Uint8Array, cellsX: number, points: TPoints) {
TIME && console.time("calculateTemperatures");
- const {cells, cellsX, points} = grid;
- const heights = cells.h;
-
const temperatures = new Int8Array(heights.length); // temperature array
// temperature decreases by 6.5 Celsius per kilometer
diff --git a/src/modules/markup.ts b/src/scripts/generation/markup.ts
similarity index 93%
rename from src/modules/markup.ts
rename to src/scripts/generation/markup.ts
index f7c01d87..3fc9dcd3 100644
--- a/src/modules/markup.ts
+++ b/src/scripts/generation/markup.ts
@@ -13,20 +13,13 @@ import {rn} from "utils/numberUtils";
const {UNMARKED, LAND_COAST, WATER_COAST, LANDLOCKED, DEEPER_WATER} = DISTANCE_FIELD;
// define features (oceans, lakes, islands)
-export function markupGridFeatures(grid: IGridWithHeights) {
+export function markupGridFeatures(neighbors: IGraphCells["c"], borderCells: IGraphCells["b"], heights: Uint8Array) {
TIME && console.time("markupGridFeatures");
Math.random = aleaPRNG(seed); // get the same result on heightmap edit in Erase mode
- if (!grid.cells || !grid.cells.h) {
- throw new Error("markupGridFeatures: grid.cells.h is required");
- }
-
- const cells = grid.cells;
- const heights = cells.h;
- const n = cells.i.length;
-
- const featureIds = new Uint16Array(n); // starts from 1
- const distanceField = new Int8Array(n);
+ const gridCellsNumber = borderCells.length;
+ const featureIds = new Uint16Array(gridCellsNumber); // starts from 1
+ const distanceField = new Int8Array(gridCellsNumber);
const features: TGridFeatures = [0];
const queue = [0];
@@ -39,9 +32,9 @@ export function markupGridFeatures(grid: IGridWithHeights) {
while (queue.length) {
const cellId = queue.pop()!;
- if (cells.b[cellId]) border = true;
+ if (borderCells[cellId]) border = true;
- for (const neighborId of cells.c[cellId]) {
+ for (const neighborId of neighbors[cellId]) {
const isNeibLand = heights[neighborId] >= MIN_LAND_HEIGHT;
if (land === isNeibLand && featureIds[neighborId] === UNMARKED) {
@@ -61,13 +54,7 @@ export function markupGridFeatures(grid: IGridWithHeights) {
}
// markup deep ocean cells
- const dfOceanMarked = markup({
- distanceField,
- neighbors: grid.cells.c,
- start: DEEPER_WATER,
- increment: -1,
- limit: -10
- });
+ const dfOceanMarked = markup({distanceField, neighbors, start: DEEPER_WATER, increment: -1, limit: -10});
TIME && console.timeEnd("markupGridFeatures");
return {featureIds, distanceField: dfOceanMarked, features};
diff --git a/src/scripts/generation/pack/pack.ts b/src/scripts/generation/pack/pack.ts
index cf9b206d..b8cb4b0f 100644
--- a/src/scripts/generation/pack/pack.ts
+++ b/src/scripts/generation/pack/pack.ts
@@ -1,6 +1,6 @@
import * as d3 from "d3";
-import {markupPackFeatures} from "modules/markup";
+import {markupPackFeatures} from "scripts/generation/markup";
// @ts-expect-error js module
import {drawScaleBar} from "modules/measurers";
// @ts-expect-error js module
diff --git a/src/scripts/generation/pack/rivers.ts b/src/scripts/generation/pack/rivers.ts
index 3ca29192..c0d39a02 100644
--- a/src/scripts/generation/pack/rivers.ts
+++ b/src/scripts/generation/pack/rivers.ts
@@ -8,7 +8,6 @@ import {getInputNumber} from "utils/nodeUtils";
import {pick} from "utils/functionUtils";
import {byId} from "utils/shorthands";
import {mergeLakeData, getClimateData, ILakeClimateData} from "./lakes";
-import {drawArrow} from "utils/debugUtils";
const {Rivers} = window;
const {LAND_COAST} = DISTANCE_FIELD;
@@ -376,8 +375,7 @@ const resolveDepressions = function (
return [initialCellHeights, {}];
}
- INFO &&
- console.info(`ⓘ Resolved all depressions. Depressions: ${depressions[0]}. Interations: ${depressions.length}`);
+ INFO && console.info(`ⓘ Resolved all depressions. Depressions: ${depressions[0]}. Iterations: ${depressions.length}`);
return [currentCellHeights, currentDrainableLakes];
// define lakes that potentially can be open (drained into another water body)
diff --git a/src/types/grid.d.ts b/src/types/grid.d.ts
index c6417078..99b06d9d 100644
--- a/src/types/grid.d.ts
+++ b/src/types/grid.d.ts
@@ -17,16 +17,6 @@ interface IGridCells {
prec: Uint8Array; // precipitation in inner units
}
-interface IGridBase extends IGrid {
- cells: IGraphCells & Partial;
- features?: TGridFeatures;
-}
-
-interface IGridWithHeights extends IGrid {
- cells: IGraphCells & Partial & {h: Uint8Array};
- features?: TGridFeatures;
-}
-
type TGridFeatures = [0, ...IGridFeature[]];
interface IGridFeature {
diff --git a/src/utils/graphUtils.ts b/src/utils/graphUtils.ts
index 1a8a9f47..963fcb1b 100644
--- a/src/utils/graphUtils.ts
+++ b/src/utils/graphUtils.ts
@@ -1,13 +1,9 @@
import {DISTANCE_FIELD, MIN_LAND_HEIGHT} from "config/generation";
-// @ts-expect-error js module
-import {aleaPRNG} from "scripts/aleaPRNG";
// return cell index on a regular square grid
export function findGridCell(x: number, y: number, grid: IGrid) {
- return (
- Math.floor(Math.min(y / grid.spacing, grid.cellsY - 1)) * grid.cellsX +
- Math.floor(Math.min(x / grid.spacing, grid.cellsX - 1))
- );
+ /* use */ const {spacing, cellsX, cellsY} = grid;
+ return Math.floor(Math.min(y / spacing, cellsY - 1)) * cellsX + Math.floor(Math.min(x / spacing, cellsX - 1));
}
// return array of cell indexes in radius on a regular square grid