From 3827a50bee5c408061fb141ed7e457eb74b34291 Mon Sep 17 00:00:00 2001 From: GoteGuru Date: Wed, 8 Jun 2022 01:10:13 +0200 Subject: [PATCH 1/5] alternative grid generators --- index.html | 13 ++++++++++ main.js | 4 ++- utils/graphUtils.js | 61 +++++++++++++++++++++++++++++++++++++++------ 3 files changed, 70 insertions(+), 8 deletions(-) diff --git a/index.html b/index.html index 60807aa0b..c55f5cd96 100644 --- a/index.html +++ b/index.html @@ -1293,6 +1293,19 @@ + + + Grid algorithm + + + + + + diff --git a/main.js b/main.js index f443111d2..8633cf4d6 100644 --- a/main.js +++ b/main.js @@ -642,8 +642,10 @@ async function generate(options) { applyMapSize(); randomizeOptions(); + const method = window[document.getElementById('gridAlgorithm').value]; + console.log('gird generation method', method); - if (shouldRegenerateGrid(grid)) grid = precreatedGraph || generateGrid(); + if (shouldRegenerateGrid(grid, method)) grid = precreatedGraph || generateGrid(method); else delete grid.cells.h; grid.cells.h = await HeightmapGenerator.generate(grid); diff --git a/utils/graphUtils.js b/utils/graphUtils.js index 75b642530..d357ff67e 100644 --- a/utils/graphUtils.js +++ b/utils/graphUtils.js @@ -2,7 +2,7 @@ // FMG utils related to graph // check if new grid graph should be generated or we can use the existing one -function shouldRegenerateGrid(grid) { +function shouldRegenerateGrid(grid, method) { const cellsDesired = +byId("pointsInput").dataset.cells; if (cellsDesired !== grid.cellsDesired) return true; @@ -10,21 +10,22 @@ function shouldRegenerateGrid(grid) { const newCellsX = Math.floor((graphWidth + 0.5 * newSpacing - 1e-10) / newSpacing); const newCellsY = Math.floor((graphHeight + 0.5 * newSpacing - 1e-10) / newSpacing); - return grid.spacing !== newSpacing || grid.cellsX !== newCellsX || grid.cellsY !== newCellsY; + return grid.generator !== method || grid.spacing !== newSpacing || grid.cellsX !== newCellsX || grid.cellsY !== newCellsY; } -function generateGrid() { +function generateGrid(generator = voronoiPoints) { Math.random = aleaPRNG(seed); // reset PRNG - const {spacing, cellsDesired, boundary, points, cellsX, cellsY} = placePoints(); + //const {spacing, cellsDesired, boundary, points, cellsX, cellsY} = placePoints(); + const {spacing, cellsDesired, boundary, points, cellsX, cellsY} = generator(); const {cells, vertices} = calculateVoronoi(points, boundary); - return {spacing, cellsDesired, boundary, points, cellsX, cellsY, cells, vertices}; + return {spacing, cellsDesired, boundary, points, cellsX, cellsY, cells, vertices, generator}; } // place random points to calculate Voronoi diagram -function placePoints() { +function voronoiPoints() { TIME && console.time("placePoints"); const cellsDesired = +byId("pointsInput").dataset.cells; - const spacing = rn(Math.sqrt((graphWidth * graphHeight) / cellsDesired), 2); // spacing between points before jirrering + const spacing = rn(Math.sqrt((graphWidth * graphHeight) / cellsDesired), 2); // spacing between points before jittering const boundary = getBoundaryPoints(graphWidth, graphHeight, spacing); const points = getJitteredGrid(graphWidth, graphHeight, spacing); // points of jittered square grid @@ -35,6 +36,52 @@ function placePoints() { return {spacing, cellsDesired, boundary, points, cellsX, cellsY}; } +// alternatively generate hex-grid +function hexPoints() { + TIME && console.time("hexPoints"); + const cellsDesired = +byId("pointsInput").dataset.cells; + const hexRatio = 0.86602540378; + const spacing = rn(Math.sqrt(graphWidth * graphHeight / hexRatio / cellsDesired), 2); // spacing between points + const lineOffset = spacing * hexRatio; + + const boundary = getBoundaryPoints(graphWidth, graphHeight, spacing); + const cellsX = Math.floor((graphWidth + 0.5 * spacing - 1e-10) / spacing); + const cellsY = Math.floor((graphHeight + 0.5 * lineOffset - 1e-10) / lineOffset); + console.log('desired hexgrid:', cellsX, cellsY); + + let points = []; + + for (let y = lineOffset / 2, lc = 0 ; y < graphHeight; y += lineOffset, lc++) { + for (let x = lc % 2 ? 0 : spacing / 2; x < graphWidth; x += spacing) { + points.push([x, y]); + } + } + + TIME && console.timeEnd("hexPoints"); + return {spacing, cellsDesired, boundary, points, cellsX, cellsY}; +} + +// square grid +function squarePoints() { + TIME && console.time("squarePoints"); + const cellsDesired = +byId("pointsInput").dataset.cells; + const spacing = rn(Math.sqrt((graphWidth * graphHeight) / cellsDesired), 2); + + const boundary = getBoundaryPoints(graphWidth, graphHeight, spacing); + const cellsX = Math.floor((graphWidth + 0.5 * spacing - 1e-10) / spacing); + const cellsY = Math.floor((graphHeight + 0.5 * spacing - 1e-10) / spacing); + + const radius = spacing / 2; + + let points = []; + for (let y = radius; y < graphHeight; y += spacing) + for (let x = radius; x < graphWidth; x += spacing) + points.push([x, y]); + + TIME && console.timeEnd("squarePoints"); + return {spacing, cellsDesired, boundary, points, cellsX, cellsY}; +} + // calculate Delaunay and then Voronoi diagram function calculateVoronoi(points, boundary) { TIME && console.time("calculateDelaunay"); From b640f977b91ac83ecbd5f436de4a006ca7d39dec Mon Sep 17 00:00:00 2001 From: GoteGuru Date: Sat, 18 Jun 2022 13:09:15 +0200 Subject: [PATCH 2/5] ignore node modules (used for testing only) --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 8cd42a0de..b7ca7adb0 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ .vscode .idea .idea/Fantasy-Map-Generator.iml +node_modules From db49f1c924ae5ba3c4aaf53bb52dc904ea3c3612 Mon Sep 17 00:00:00 2001 From: GoteGuru Date: Sun, 19 Jun 2022 00:11:46 +0200 Subject: [PATCH 3/5] refactoring for row based code --- index.html | 7 ++++--- main.js | 11 ++++++++--- modules/io/load.js | 1 + modules/io/save.js | 3 ++- modules/submap.js | 2 +- modules/ui/submap.js | 5 ++++- utils/graphUtils.js | 42 ++++++++++++++++++++++++++++++------------ 7 files changed, 50 insertions(+), 21 deletions(-) diff --git a/index.html b/index.html index 143b0cdc4..8493b0e88 100644 --- a/index.html +++ b/index.html @@ -1505,9 +1505,10 @@ Grid algorithm diff --git a/main.js b/main.js index 8633cf4d6..78c89dd12 100644 --- a/main.js +++ b/main.js @@ -631,6 +631,7 @@ void (function addDragToUpload() { }); })(); +const gridOptimizationRequired = () => window[document.getElementById('gridAlgorithm').value] == voronoiPoints; async function generate(options) { try { const timeStart = performance.now(); @@ -1166,23 +1167,27 @@ function generatePrecipitation() { } // recalculate Voronoi Graph to pack cells +// pars: optimize -> optimize cells structure, copy original graph otherwise. function reGraph() { TIME && console.time("reGraph"); const {cells: gridCells, points, features} = grid; const newCells = {p: [], g: [], h: []}; // store new data const spacing2 = grid.spacing ** 2; + const optimize = gridOptimizationRequired() for (const i of gridCells.i) { const height = gridCells.h[i]; const type = gridCells.t[i]; - if (height < 20 && type !== -1 && type !== -2) continue; // exclude all deep ocean points - if (type === -2 && (i % 4 === 0 || features[gridCells.f[i]].type === "lake")) continue; // exclude non-coastal lake points + if (optimize) { + if (height < 20 && type !== -1 && type !== -2) continue; // exclude all deep ocean points + if (type === -2 && (i % 4 === 0 || features[gridCells.f[i]].type === "lake")) continue; // exclude non-coastal lake points + } const [x, y] = points[i]; addNewPoint(i, x, y, height); // add additional points for cells along coast - if (type === 1 || type === -1) { + if (optimize && (type === 1 || type === -1)) { if (gridCells.b[i]) continue; // not for near-border cells gridCells.c[i].forEach(function (e) { if (i > e) return; diff --git a/modules/io/load.js b/modules/io/load.js index 946df93f4..c6dbf26bc 100644 --- a/modules/io/load.js +++ b/modules/io/load.js @@ -229,6 +229,7 @@ async function parseLoadedData(data) { if (settings[22]) stylePreset.value = settings[22]; if (settings[23]) rescaleLabels.checked = +settings[23]; if (settings[24]) urbanDensity = urbanDensityInput.value = urbanDensityOutput.value = +settings[24]; + if (settings[25]) gridAlgorithm.value = settings[25]; })(); void (function applyOptionsToUI() { diff --git a/modules/io/save.js b/modules/io/save.js index db303eff5..211c7f6d5 100644 --- a/modules/io/save.js +++ b/modules/io/save.js @@ -34,7 +34,8 @@ function getMapData() { +hideLabels.checked, stylePreset.value, +rescaleLabels.checked, - urbanDensity + urbanDensity, + byId('gridAlgorithm').value, ].join("|"); const coords = JSON.stringify(mapCoordinates); const biomes = [biomesData.color, biomesData.habitability, biomesData.name].join("|"); diff --git a/modules/submap.js b/modules/submap.js index 854084872..ddd195c60 100644 --- a/modules/submap.js +++ b/modules/submap.js @@ -40,7 +40,7 @@ window.Submap = (function () { // create new grid applyMapSize(); - grid = generateGrid(); + grid = generateGrid(options.gridAlgorithm); drawScaleBar(scale); diff --git a/modules/ui/submap.js b/modules/ui/submap.js index 737560c38..c8781d344 100644 --- a/modules/ui/submap.js +++ b/modules/ui/submap.js @@ -169,6 +169,7 @@ window.UISubmap = (function () { WARN && console.warn("Resampling current map"); const cellNumId = +byId("submapPointsInput").value; if (!cellsDensityMap[cellNumId]) return console.error("Unknown cell number!"); + const gridAlgorithm = window[document.getElementById('gridAlgorithm').value]; const {angle, shiftX, shiftY, ratio, mirrorH, mirrorV} = getTransformInput(); @@ -203,6 +204,7 @@ window.UISubmap = (function () { smoothHeightMap: false, rescaleStyles: false, scale: 1, + gridAlgorithm, projection, inverse }); @@ -229,7 +231,8 @@ window.UISubmap = (function () { smoothHeightMap: scale > 2, inverse: (x, y) => [x / origScale + x0, y / origScale + y0], projection: (x, y) => [(x - x0) * origScale, (y - y0) * origScale], - scale: origScale + scale: origScale, + gridAlgorithm: window[byId('gridAlgorithm').value], }; // converting map position on the planet diff --git a/utils/graphUtils.js b/utils/graphUtils.js index d357ff67e..8e033a1e6 100644 --- a/utils/graphUtils.js +++ b/utils/graphUtils.js @@ -37,28 +37,34 @@ function voronoiPoints() { } // alternatively generate hex-grid -function hexPoints() { +function hexPointsP() { return hexPoints(true) } +function hexPointsF() { return hexPoints(false) } +const hexRatio = Math.sqrt(3)/2; +function hexPoints(pointy) { // pointy must be 0 or 1 TIME && console.time("hexPoints"); const cellsDesired = +byId("pointsInput").dataset.cells; - const hexRatio = 0.86602540378; - const spacing = rn(Math.sqrt(graphWidth * graphHeight / hexRatio / cellsDesired), 2); // spacing between points - const lineOffset = spacing * hexRatio; + let spacing = rn(Math.sqrt(graphWidth * graphHeight / hexRatio / cellsDesired), 2); // spacing between points + let xSpacing, ySpacing; + if (pointy) { + xSpacing = spacing; + ySpacing = spacing * hexRatio; + } else { + xSpacing = spacing * hexRatio * 2; + ySpacing = spacing / 2 ; + } const boundary = getBoundaryPoints(graphWidth, graphHeight, spacing); - const cellsX = Math.floor((graphWidth + 0.5 * spacing - 1e-10) / spacing); - const cellsY = Math.floor((graphHeight + 0.5 * lineOffset - 1e-10) / lineOffset); - console.log('desired hexgrid:', cellsX, cellsY); - let points = []; - for (let y = lineOffset / 2, lc = 0 ; y < graphHeight; y += lineOffset, lc++) { - for (let x = lc % 2 ? 0 : spacing / 2; x < graphWidth; x += spacing) { + let rc, lc, x, y; + for (y = ySpacing / 2, lc = 0 ; y < graphHeight; y += ySpacing, lc++) { + for (x = lc % 2 ? 0 : xSpacing / 2, rc=0; x < graphWidth; x += xSpacing, rc++) { points.push([x, y]); } } TIME && console.timeEnd("hexPoints"); - return {spacing, cellsDesired, boundary, points, cellsX, cellsY}; + return {spacing, cellsDesired, boundary, points, cellsX: rc, cellsY: lc}; } // square grid @@ -143,7 +149,19 @@ function getJitteredGrid(width, height, spacing) { // return cell index on a regular square grid function findGridCell(x, y, grid) { - return Math.floor(Math.min(y / grid.spacing, grid.cellsY - 1)) * grid.cellsX + Math.floor(Math.min(x / grid.spacing, grid.cellsX - 1)); + let xSpacing = grid.spacing; + let ySpacing = grid.spacing * Math.sqrt(3) / 2; + const maxindex = grid.cells.i.length; // safety belt + switch (grid.generator) { + case voronoiPoints: + case squarePoints: + return Math.floor(Math.min(y / grid.spacing, grid.cellsY - 1)) * grid.cellsX + Math.floor(Math.min(x / grid.spacing, grid.cellsX - 1)); + case hexPointsF: + xSpacing = grid.spacing * hexRatio; + ySpacing = grid.spacing / 2; + case hexPointsP: + return Math.min(Math.floor(Math.min(y / ySpacing + 1e-10, grid.cellsY - 1)) * grid.cellsX + Math.floor(Math.min(x / xSpacing + 1e-10, grid.cellsX - 1)), maxindex); + } } // return array of cell indexes in radius on a regular square grid From 4291777bc446c688f50d72e1c4b22afa02f11a52 Mon Sep 17 00:00:00 2001 From: GoteGuru Date: Sun, 19 Jun 2022 00:19:02 +0200 Subject: [PATCH 4/5] update versions --- index.html | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/index.html b/index.html index 8493b0e88..5bb5f0c4b 100644 --- a/index.html +++ b/index.html @@ -7795,7 +7795,7 @@ - + @@ -7819,7 +7819,7 @@ - + @@ -7866,14 +7866,14 @@ - + - - + + From f4b3662cba0b70e5bf4a0978bb7eed25882b4e38 Mon Sep 17 00:00:00 2001 From: GoteGuru Date: Mon, 20 Jun 2022 18:39:50 +0200 Subject: [PATCH 5/5] refactor and fix saved grid algo. --- index.html | 4 +-- main.js | 8 ++--- modules/dynamic/heightmap-selection.js | 4 ++- modules/io/save.js | 2 +- modules/submap.js | 2 +- modules/ui/submap.js | 7 +++-- utils/graphUtils.js | 41 +++++++++++++------------- 7 files changed, 36 insertions(+), 32 deletions(-) diff --git a/index.html b/index.html index 5bb5f0c4b..ae2335349 100644 --- a/index.html +++ b/index.html @@ -1502,10 +1502,10 @@ - Grid algorithm + Grid type