diff --git a/main.js b/main.js index 68de3a38..5ad4f637 100644 --- a/main.js +++ b/main.js @@ -542,6 +542,7 @@ function generate() { drawScaleBar(); HeightmapGenerator.generate(); markFeatures(); + getSignedDistanceField(); openNearSeaLakes(); OceanLayers(); defineMapSize(); @@ -641,17 +642,17 @@ function calculateVoronoi(graph, points) { TIME && console.timeEnd("calculateVoronoi"); } -// Mark features (ocean, lakes, islands) +// Mark features (ocean, lakes, islands) and calculate distance field function markFeatures() { TIME && console.time("markFeatures"); - Math.random = aleaPRNG(seed); // restart Math.random() to get the same result on heightmap edit in Erase mode + Math.random = aleaPRNG(seed); // get the same result on heightmap edit in Erase mode const cells = grid.cells, heights = grid.cells.h; cells.f = new Uint16Array(cells.i.length); // cell feature number - cells.t = new Int8Array(cells.i.length); // cell type: 1 = land coast; -1 = water near coast; + cells.t = new Int8Array(cells.i.length); // cell type: 1 = land coast; -1 = water near coast grid.features = [0]; - for (let i=1, queue=[0]; queue[0] !== -1; i++) { + for (let i = 1, queue = [0]; queue[0] !== -1; i++) { cells.f[queue[0]] = i; // feature number const land = heights[queue[0]] >= 20; let border = false; // true if feature touches map border @@ -659,16 +660,15 @@ function markFeatures() { while (queue.length) { const q = queue.pop(); if (cells.b[q]) border = true; - cells.c[q].forEach(function(e) { - const eLand = heights[e] >= 20; - //if (eLand) cells.t[e] = 2; - if (land === eLand && cells.f[e] === 0) { - cells.f[e] = i; - queue.push(e); - } - if (land && !eLand) { + + cells.c[q].forEach(c => { + const cLand = heights[c] >= 20; + if (land === cLand && !cells.f[c]) { + cells.f[c] = i; + queue.push(c); + } else if (land && !cLand) { cells.t[q] = 1; - cells.t[e] = -1; + cells.t[c] = -1; } }); } @@ -681,6 +681,31 @@ function markFeatures() { TIME && console.timeEnd("markFeatures"); } +function getSignedDistanceField() { + TIME && console.time("getSignedDistanceField"); + const cells = grid.cells, pointsN = cells.i.length; + markup(-2, -1, -10); + markup(2, 1, 0); + + // build signed distance field + function markup(start, increment, limit) { + for (let t = start, count = Infinity; count > 0 && t > limit; t += increment) { + count = 0; + const prevT = t - increment; + for (let i = 0; i < pointsN; i++) { + if (cells.t[i] !== prevT) continue; + + for (const c of cells.c[i]) { + if (cells.t[c]) continue; + cells.t[c] = t; + count++; + } + } + } + } + TIME && console.timeEnd("getSignedDistanceField"); +} + // 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 @@ -915,18 +940,22 @@ function generatePrecipitation() { // recalculate Voronoi Graph to pack cells function reGraph() { TIME && console.time("reGraph"); - let cells = grid.cells, points = grid.points, features = grid.features; - const newCells = {p:[], g:[], h:[], t:[], f:[], r:[], biome:[]}; // to store new data + let {cells, points, features} = grid; + const newCells = {p:[], g:[], h:[]}; // to store new data const spacing2 = grid.spacing ** 2; + console.log("Graph points:", points); + console.log("Graph T points:", cells.t); + for (const i of cells.i) { const height = cells.h[i]; const type = cells.t[i]; if (height < 20 && type !== -1 && type !== -2) continue; // exclude all deep ocean points if (type === -2 && (i%4=== 0 || features[cells.f[i]].type === "lake")) continue; // exclude non-coastal lake points - const x = points[i][0], y = points[i][1]; + const [x, y] = points[i]; + + addNewPoint(i, x, y, height); - addNewPoint(x, y); // add point to array // add additional points for cells along coast if (type === 1 || type === -1) { if (cells.b[i]) continue; // not for near-border cells @@ -937,18 +966,20 @@ function reGraph() { if (dist2 < spacing2) return; // too close to each other const x1 = rn((x + points[e][0]) / 2, 1); const y1 = rn((y + points[e][1]) / 2, 1); - addNewPoint(x1, y1); + addNewPoint(i, x1, y1, height); } }); } - - function addNewPoint(x, y) { - newCells.p.push([x, y]); - newCells.g.push(i); - newCells.h.push(height); - } } + function addNewPoint(i, x, y, height) { + newCells.p.push([x, y]); + newCells.g.push(i); + newCells.h.push(height); + } + + console.log("New points:", newCells.p); + calculateVoronoi(pack, newCells.p); cells = pack.cells; cells.p = newCells.p; // points coordinates [x, y] @@ -1068,9 +1099,9 @@ function drawCoastline() { // Re-mark features (ocean, lakes, islands) function reMarkFeatures() { TIME && console.time("reMarkFeatures"); - const cells = pack.cells, features = pack.features = [0], temp = grid.cells.temp; + const cells = pack.cells, features = pack.features = [0]; cells.f = new Uint16Array(cells.i.length); // cell feature number - cells.t = new Int16Array(cells.i.length); // cell type: 1 = land along coast; -1 = water along coast; + cells.t = new Int8Array(cells.i.length); // cell type: 1 = land along coast; -1 = water along coast; cells.haven = cells.i.length < 65535 ? new Uint16Array(cells.i.length) : new Uint32Array(cells.i.length);// cell haven (opposite water cell); cells.harbor = new Uint8Array(cells.i.length); // cell harbor (number of adjacent water cells); diff --git a/modules/ocean-layers.js b/modules/ocean-layers.js index a39bbdd7..371e88cc 100644 --- a/modules/ocean-layers.js +++ b/modules/ocean-layers.js @@ -14,7 +14,6 @@ lineGen.curve(d3.curveBasisClosed); cells = grid.cells, pointsN = grid.cells.i.length, vertices = grid.vertices; const limits = outline === "random" ? randomizeOutline() : outline.split(",").map(s => +s); - markupOcean(limits); const chains = []; const opacity = rn(0.4 / limits.length, 2); @@ -33,15 +32,12 @@ 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); - //const inside = d3.polygonContains(points, grid.points[i]); - chains.push([t, points]); //chains.push([t, points, inside]); + chains.push([t, points]); } - //const bbox = `M0,0h${graphWidth}v${graphHeight}h${-graphWidth}Z`; for (const t of limits) { const layer = chains.filter(c => c[0] === t); let path = layer.map(c => round(lineGen(c[1]))).join(""); - //if (layer.every(c => !c[2])) path = bbox + path; // add outer ring if all segments are outside (works not for all cases) if (path) oceanLayers.append("path").attr("d", path).attr("fill", "#ecf2f9").style("opacity", opacity); } @@ -64,16 +60,6 @@ return limits; } - // Define grid ocean cells type based on distance form land - function markupOcean(limits) { - for (let t = -2; t >= limits[0]-1; t--) { - for (let i = 0; i < pointsN; i++) { - if (cells.t[i] !== t+1) continue; - cells.c[i].forEach(function(e) {if (!cells.t[e]) cells.t[e] = t;}); - } - } - } - // connect vertices to chain function connectVertices(start, t) { const chain = []; // vertices chain to form a path diff --git a/modules/river-generator.js b/modules/river-generator.js index 002b0296..e231871e 100644 --- a/modules/river-generator.js +++ b/modules/river-generator.js @@ -15,7 +15,6 @@ const generate = function(changeHeights = true) { cells.conf = new Uint8Array(cells.i.length); // confluences array let riverNext = 1; // first river id is 1 - markupLand(); const h = alterHeights(); removeStoredLakeData(); resolveDepressions(h); @@ -27,16 +26,6 @@ const generate = function(changeHeights = true) { TIME && console.timeEnd('generateRivers'); - // build distance field in cells from water (cells.t) - function markupLand() { - const q = t => cells.i.filter(i => cells.t[i] === t); - for (let t = 2, queue = q(t); queue.length; t++, queue = q(t)) { - queue.forEach(i => cells.c[i].forEach(c => { - if (!cells.t[c]) cells.t[c] = t+1; - })); - } - } - // height with added t value to make map less depressed function alterHeights() { const h = Array.from(cells.h) diff --git a/modules/ui/heightmap-editor.js b/modules/ui/heightmap-editor.js index f599dd64..f62d2ab0 100644 --- a/modules/ui/heightmap-editor.js +++ b/modules/ui/heightmap-editor.js @@ -169,6 +169,7 @@ function editHeightmap() { const change = changeHeights.checked; markFeatures(); + getSignedDistanceField(); if (change) openNearSeaLakes(); OceanLayers(); calculateTemperatures(); @@ -284,6 +285,7 @@ function editHeightmap() { }); markFeatures(); + getSignedDistanceField(); OceanLayers(); calculateTemperatures(); generatePrecipitation();