diff --git a/index.css b/index.css index f070c939..1b7a983b 100644 --- a/index.css +++ b/index.css @@ -922,7 +922,7 @@ fieldset { display: inline-block; } -body button.noicon { +#templateTools > button { width: 1.8em; height: 1.6em; margin: 1px; diff --git a/index.html b/index.html index 2a5e9214..e6883666 100644 --- a/index.html +++ b/index.html @@ -107,7 +107,7 @@ } } - + @@ -1292,7 +1292,7 @@ - + @@ -1302,6 +1302,7 @@ + @@ -1758,13 +1759,13 @@
- Depressions filling max iterations: +
Depressions filling max iterations:
- Depression depth threshold: +
Depression depth threshold:
@@ -2932,13 +2933,13 @@ @@ -6109,8 +6116,8 @@ - - + + @@ -6140,7 +6147,7 @@ - + diff --git a/modules/heightmap-generator.js b/modules/heightmap-generator.js index 0723a34a..762350ce 100644 --- a/modules/heightmap-generator.js +++ b/modules/heightmap-generator.js @@ -26,7 +26,7 @@ window.HeightmapGenerator = (function () { // load heightmap into image and render to canvas const img = new Image(); img.src = `./heightmaps/${input.value}.png`; - img.onload = function () { + img.onload = () => { ctx.drawImage(img, 0, 0, cellsX, cellsY); const imageData = ctx.getImageData(0, 0, cellsX, cellsY); assignColorsToHeight(imageData.data); @@ -61,6 +61,7 @@ window.HeightmapGenerator = (function () { if (a1 === "Range") return addRange(a2, a3, a4, a5); if (a1 === "Trough") return addTrough(a2, a3, a4, a5); if (a1 === "Strait") return addStrait(a2, a3); + if (a1 === "Mask") return mask(a2); if (a1 === "Add") return modify(a3, +a2, 1); if (a1 === "Multiply") return modify(a3, 0, +a2); if (a1 === "Smooth") return smooth(a2); @@ -100,7 +101,7 @@ window.HeightmapGenerator = (function () { if (cells === 100000) return 0.93; } - const addHill = function (count, height, rangeX, rangeY) { + const addHill = (count, height, rangeX, rangeY) => { count = getNumberInRange(count); const power = getBlobPower(); while (count > 0) { @@ -137,7 +138,7 @@ window.HeightmapGenerator = (function () { } }; - const addPit = function (count, height, rangeX, rangeY) { + const addPit = (count, height, rangeX, rangeY) => { count = getNumberInRange(count); while (count > 0) { addOnePit(); @@ -173,7 +174,7 @@ window.HeightmapGenerator = (function () { } }; - const addRange = function (count, height, rangeX, rangeY) { + const addRange = (count, height, rangeX, rangeY) => { count = getNumberInRange(count); const power = getLinePower(); while (count > 0) { @@ -259,7 +260,7 @@ window.HeightmapGenerator = (function () { } }; - const addTrough = function (count, height, rangeX, rangeY) { + const addTrough = (count, height, rangeX, rangeY) => { count = getNumberInRange(count); const power = getLinePower(); while (count > 0) { @@ -354,7 +355,7 @@ window.HeightmapGenerator = (function () { } }; - const addStrait = function (width, direction = "vertical") { + const addStrait = (width, direction = "vertical") => { width = Math.min(getNumberInRange(width), grid.cellsX / 3); if (width < 1 && P(width)) return; const used = new Uint8Array(cells.h.length); @@ -364,8 +365,8 @@ window.HeightmapGenerator = (function () { const endX = vert ? Math.floor(graphWidth - startX - graphWidth * 0.1 + Math.random() * graphWidth * 0.2) : graphWidth - 5; const endY = vert ? graphHeight - 5 : Math.floor(graphHeight - startY - graphHeight * 0.1 + Math.random() * graphHeight * 0.2); - const start = findGridCell(startX, startY), - end = findGridCell(endX, endY); + const start = findGridCell(startX, startY); + const end = findGridCell(endX, endY); let range = getRange(start, end); const query = []; @@ -407,7 +408,7 @@ window.HeightmapGenerator = (function () { } }; - const modify = function (range, add, mult, power) { + const modify = (range, add, mult, power) => { const min = range === "land" ? 20 : range === "all" ? 0 : +range.split("-")[0]; const max = range === "land" || range === "all" ? 100 : +range.split("-")[1]; const isLand = min === 20; @@ -422,14 +423,49 @@ window.HeightmapGenerator = (function () { }); }; - const smooth = function (fr = 2, add = 0) { + const smooth = (fr = 2, add = 0) => { cells.h = cells.h.map((h, i) => { const a = [h]; cells.c[i].forEach(c => a.push(cells.h[c])); + if (fr === 1) return d3.mean(a) + add; return lim((h * (fr - 1) + d3.mean(a) + add) / fr); }); }; + const mask = (power = 1) => { + const fr = power ? Math.abs(power) : 1; + + cells.h = cells.h.map((h, i) => { + const [x, y] = p[i]; + const nx = (2 * x) / graphWidth - 1; // [-1, 1], 0 is center + const ny = (2 * y) / graphHeight - 1; // [-1, 1], 0 is center + let distance = (1 - nx ** 2) * (1 - ny ** 2); // 1 is center, 0 is edge + if (power < 0) distance = 1 - distance; // inverted, 0 is center, 1 is edge + const masked = h * distance; + return lim((h * (fr - 1) + masked) / fr); + }); + }; + + const invert = (count, axes) => { + if (!P(count)) return; + + const invertX = axes !== "y"; + const invertY = axes !== "x"; + const {cellsX, cellsY} = grid; + + const inverted = cells.h.map((h, i) => { + const x = i % cellsX; + const y = Math.floor(i / cellsX); + + const nx = invertX ? cellsX - x - 1 : x; + const ny = invertY ? cellsY - y - 1 : y; + const invertedI = nx + ny * cellsX; + return cells.h[invertedI]; + }); + + cells.h = inverted; + }; + function getPointInRange(range, length) { if (typeof range !== "string") { ERROR && console.error("Range should be a string"); @@ -449,5 +485,5 @@ window.HeightmapGenerator = (function () { } } - return {generate, addHill, addRange, addTrough, addStrait, addPit, smooth, modify}; + return {generate, addHill, addRange, addTrough, addStrait, addPit, smooth, modify, mask, invert}; })(); diff --git a/modules/heightmap-templates.js b/modules/heightmap-templates.js index bd371e10..7e0cd20c 100644 --- a/modules/heightmap-templates.js +++ b/modules/heightmap-templates.js @@ -4,49 +4,55 @@ window.HeightmapTemplates = (function () { const volcano = `Hill 1 90-100 44-56 40-60 Multiply 0.8 50-100 0 0 Range 1.5 30-55 45-55 40-60 - Smooth 2 0 0 0 - Hill 1.5 25-35 25-30 20-75 - Hill 1 25-35 75-80 25-75 - Hill 0.5 20-25 10-15 20-25`; + Smooth 3 0 0 0 + Hill 1.5 35-45 25-30 20-75 + Hill 1 35-55 75-80 25-75 + Hill 0.5 20-25 10-15 20-25 + Mask 3 0 0 0`; const highIsland = `Hill 1 90-100 65-75 47-53 - Add 5 all 0 0 - Hill 6 20-23 25-55 45-55 + Add 7 all 0 0 + Hill 5-6 20-30 25-55 45-55 Range 1 40-50 45-55 45-55 + Multiply 0.8 land 0 0 + Mask 3 0 0 0 Smooth 2 0 0 0 Trough 2-3 20-30 20-30 20-30 Trough 2-3 20-30 60-80 70-80 Hill 1 10-15 60-60 50-50 Hill 1.5 13-16 15-20 20-75 - Multiply 0.8 20-100 0 0 Range 1.5 30-40 15-85 30-40 Range 1.5 30-40 15-85 60-70 - Pit 2-3 10-15 15-85 20-80`; + Pit 3-5 10-30 15-85 20-80`; const lowIsland = `Hill 1 90-99 60-80 45-55 - Hill 4-5 25-35 20-65 40-60 + Hill 1-2 20-30 10-30 10-90 + Smooth 2 0 0 0 + Hill 6-7 25-35 20-70 30-70 Range 1 40-50 45-55 45-55 - Smooth 3 0 0 0 - Trough 1.5 20-30 15-85 20-30 - Trough 1.5 20-30 15-85 70-80 + Trough 2-3 20-30 15-85 20-30 + Trough 2-3 20-30 15-85 70-80 Hill 1.5 10-15 5-15 20-80 Hill 1 10-15 85-95 70-80 - Pit 3-5 10-15 15-85 20-80 - Multiply 0.4 20-100 0 0`; + Pit 5-7 15-25 15-85 20-80 + Multiply 0.4 20-100 0 0 + Mask 4 0 0 0`; - const continents = `Hill 1 80-85 75-80 40-60 - Hill 1 80-85 20-25 40-60 - Multiply 0.22 20-100 0 0 - Hill 5-6 15-20 25-75 20-82 - Range .8 30-60 5-15 20-45 - Range .8 30-60 5-15 55-80 + const continents = `Hill 1 80-85 60-80 40-60 + Hill 1 80-85 20-30 40-60 + Hill 6-7 15-30 25-75 15-85 + Multiply 0.6 land 0 0 + Hill 8-10 5-10 15-85 20-80 + Range 1-2 30-60 5-15 25-75 + Range 1-2 30-60 80-95 25-75 Range 0-3 30-60 80-90 20-80 - Trough 3-4 15-20 15-85 20-80 Strait 2 vertical 0 0 - Smooth 2 0 0 0 - Trough 1-2 5-10 45-55 45-55 - Pit 3-4 10-15 15-85 20-80 - Hill 1 5-10 40-60 40-60`; + Strait 1 vertical 0 0 + Smooth 3 0 0 0 + Trough 3-4 15-20 15-85 20-80 + Trough 3-4 5-10 45-55 45-55 + Pit 3-4 10-20 15-85 20-80 + Mask 4 0 0 0`; const archipelago = `Add 11 all 0 0 Range 2-3 40-60 20-80 20-80 @@ -63,18 +69,19 @@ window.HeightmapTemplates = (function () { Hill .5 30-50 25-35 30-70 Smooth 1 0 0 0 Multiply 0.2 25-100 0 0 - Hill .5 10-20 50-55 48-52`; + Hill 0.5 10-20 50-55 48-52`; - const mediterranean = `Range 3-4 30-50 0-100 0-10 - Range 3-4 30-50 0-100 90-100 - Hill 5-6 30-70 0-100 0-5 - Hill 5-6 30-70 0-100 95-100 + const mediterranean = `Range 4-6 30-80 0-100 0-10 + Range 4-6 30-80 0-100 90-100 + Hill 6-8 30-50 10-90 0-5 + Hill 6-8 30-50 10-90 95-100 + Multiply 0.9 land 0 0 + Mask -2 0 0 0 Smooth 1 0 0 0 Hill 2-3 30-70 0-5 20-80 Hill 2-3 30-70 95-100 20-80 - Multiply 0.8 land 0 0 - Trough 3-5 40-50 0-100 0-10 - Trough 3-5 40-50 0-100 90-100`; + Trough 3-6 40-50 0-100 0-10 + Trough 3-6 40-50 0-100 90-100`; const peninsula = `Range 2-3 20-35 40-50 0-15 Add 5 all 0 0 @@ -83,7 +90,8 @@ window.HeightmapTemplates = (function () { Hill 3-4 3-5 5-95 80-100 Hill 1-2 3-5 5-95 40-60 Trough 5-6 10-25 5-95 5-95 - Smooth 3 0 0 0`; + Smooth 3 0 0 0 + Invert 0.4 both 0 0`; const pangea = `Hill 1-2 25-40 15-50 0-10 Hill 1-2 5-40 50-85 0-10 @@ -106,7 +114,8 @@ window.HeightmapTemplates = (function () { Trough 4-8 15-30 10-50 20-40 Trough 4-8 15-30 30-70 40-60 Trough 4-8 15-30 50-90 60-80 - Trough 4-8 15-30 70-100 80-100`; + Trough 4-8 15-30 70-100 80-100 + Invert 0.25 x 0 0`; const shattered = `Hill 8 35-40 15-85 30-70 Trough 10-20 40-50 5-95 5-95 @@ -117,58 +126,42 @@ window.HeightmapTemplates = (function () { Hill 2-4 60-85 0-5 0-100 Hill 2-4 60-85 95-100 0-100 Hill 3-4 60-85 20-80 0-5 - Hill 3-4 60-85 20-80 95-100`; + Hill 3-4 60-85 20-80 95-100 + Smooth 3 0 0 0`; - - const oldWorld = `Hill 4-6 20-40 15-85 30-45 - Hill 3-7 20-40 15-85 55-70 - Strait 2-7 vertical 0 0 - Pit 1-2 40-50 35-55 20-80 - Strait 2-7 vertical 0 0 - Range 2-3 20-25 15-35 20-30 - Range 2-3 20-25 15-35 65-80 - Range 2-3 20-25 45-85 20-45 - Range 2-3 20-25 45-85 65-80 - Multiply .9 80-100 0 0 - Strait 2-7 vertical 0 0 - Pit 2-3 40-50 45-65 20-80 - Trough 1-2 40-50 15-45 20-45 - Trough 1-3 40-50 15-45 45-80 - Trough 1-2 40-50 45-85 20-45 - Trough 1-2 40-50 45-85 45-80 - Multiply 1.2 17-20 0 0 - Strait 2-7 horizontal 0 0 - Multiply 1.2 17-50 0 0 - Range 1-2 20-25 15-45 45-65 - Range 1-2 20-25 65-85 45-80 - Multiply 1.1 50-80 0 0 - Hill 1-2 20 15-45 20-80 - Hill 1-2 20 65-85 20-80 - Multiply 1.2 15-30 0 0 - Strait 2-7 vertical 0 0 - Trough 1-2 40-50 35-65 65-80 - Range 1-2 20-25 15-35 20-45 - Strait 2-7 vertical 0 0 - Range 1-2 20-25 65-85 45-80 - Multiply .9 70-100 0 0 - Hill 1-2 20-25 15-45 65-80 - Hill 1-2 20-25 65-85 20-45 - Hill 1 20-25 15-45 45-65 - Hill 1 20-25 65-85 45-65 - Strait 2-7 vertical 0 0 - Trough 1-2 20-50 15-45 45-65 - Trough 1-2 20-50 65-85 45-65 - Strait 2-7 horizontal 0 0 - Multiply 0.8 70-100 0 0 - Hill 1-2 20-25 35-45 45-65 - Hill 1-2 20-25 65-70 45-65 - Pit 2-3 40-50 45-65 30-70 - Trough 1-2 40-50 15-85 65-80 - Trough 1-2 40-50 15-85 10-35 - Strait 2-5 vertical 0 0 - Multiply 1.1 45-90 0 0 + const oldWorld = `Range 3 70 15-85 20-80 + Hill 2-3 50-70 15-45 20-80 + Hill 2-3 50-70 65-85 20-80 + Hill 4-6 20-25 15-85 20-80 + Multiply 0.5 land 0 0 + Smooth 2 0 0 0 + Range 3-4 20-50 15-35 20-45 + Range 2-4 20-50 65-85 45-80 Strait 3-7 vertical 0 0 - Trough 1-2 40-50 45-65 45-65`; + Trough 6-8 20-50 15-85 45-65 + Pit 5-6 20-30 10-90 10-90`; - return {volcano, highIsland, lowIsland, continents, archipelago, atoll, mediterranean, peninsula, peninsula, pangea, isthmus, shattered, taklamakan, oldWorld}; + const fractious = `Hill 12-15 50-80 5-95 5-95 + Mask -1.5 0 0 0 + Mask 3 0 0 0 + Add -20 30-100 0 0 + Range 6-8 40-50 5-95 10-90`; + + return { + volcano, + highIsland, + lowIsland, + continents, + archipelago, + atoll, + mediterranean, + peninsula, + peninsula, + pangea, + isthmus, + shattered, + taklamakan, + oldWorld, + fractious + }; })(); diff --git a/modules/ui/heightmap-editor.js b/modules/ui/heightmap-editor.js index fae944f3..12d8b283 100644 --- a/modules/ui/heightmap-editor.js +++ b/modules/ui/heightmap-editor.js @@ -34,19 +34,19 @@ function editHeightmap() { modules.editHeightmap = true; // add listeners - document.getElementById("paintBrushes").addEventListener("click", openBrushesPanel); - document.getElementById("applyTemplate").addEventListener("click", openTemplateEditor); - document.getElementById("convertImage").addEventListener("click", openImageConverter); - document.getElementById("heightmapPreview").addEventListener("click", toggleHeightmapPreview); - document.getElementById("heightmap3DView").addEventListener("click", changeViewMode); - document.getElementById("finalizeHeightmap").addEventListener("click", finalizeHeightmap); - document.getElementById("renderOcean").addEventListener("click", mockHeightmap); - document.getElementById("templateUndo").addEventListener("click", () => restoreHistory(edits.n - 1)); - document.getElementById("templateRedo").addEventListener("click", () => restoreHistory(edits.n + 1)); + byId("paintBrushes").on("click", openBrushesPanel); + byId("applyTemplate").on("click", openTemplateEditor); + byId("convertImage").on("click", openImageConverter); + byId("heightmapPreview").on("click", toggleHeightmapPreview); + byId("heightmap3DView").on("click", changeViewMode); + byId("finalizeHeightmap").on("click", finalizeHeightmap); + byId("renderOcean").on("click", mockHeightmap); + byId("templateUndo").on("click", () => restoreHistory(edits.n - 1)); + byId("templateRedo").on("click", () => restoreHistory(edits.n + 1)); function enterHeightmapEditMode(type) { editHeightmap.layers = Array.from(mapLayers.querySelectorAll("li:not(.buttonoff)")).map(node => node.id); // store layers preset - editHeightmap.layers.forEach(l => document.getElementById(l).click()); // turn off all layers + editHeightmap.layers.forEach(l => byId(l).click()); // turn off all layers customization = 1; closeDialogs(); @@ -113,7 +113,7 @@ function editHeightmap() { if (tooltip.dataset.main) showMainTip(); // move radius circle if drag mode is active - const pressed = document.getElementById("brushesButtons").querySelector("button.pressed"); + const pressed = byId("brushesButtons").querySelector("button.pressed"); if (!pressed) return; moveCircle(p[0], p[1], brushRadius.valueAsNumber, "#333"); } @@ -137,7 +137,7 @@ function editHeightmap() { function finalizeHeightmap() { if (viewbox.select("#heights").selectAll("*").size() < 200) return tip("Insufficient land area! There should be at least 200 land cells to finalize the heightmap", null, "error"); - if (document.getElementById("imageConverter").offsetParent) return tip("Please exit the Image Conversion mode first", null, "error"); + if (byId("imageConverter").offsetParent) return tip("Please exit the Image Conversion mode first", null, "error"); delete window.edits; // remove global variable redo.disabled = templateRedo.disabled = true; @@ -145,7 +145,7 @@ function editHeightmap() { customization = 0; customizationMenu.style.display = "none"; - if (document.getElementById("options").querySelector(".tab > button.active").id === "toolsTab") toolsContent.style.display = "block"; + if (byId("options").querySelector(".tab > button.active").id === "toolsTab") toolsContent.style.display = "block"; layersPreset.disabled = false; exitCustomization.style.display = "none"; // hide finalize button restoreDefaultEvents(); @@ -153,8 +153,8 @@ function editHeightmap() { closeDialogs(); resetZoom(); - if (document.getElementById("preview")) document.getElementById("preview").remove(); - if (document.getElementById("canvas3d")) enterStandardView(); + if (byId("preview")) byId("preview").remove(); + if (byId("canvas3d")) enterStandardView(); const mode = heightmapEditMode.innerHTML; if (mode === "erase") regenerateErasedData(); @@ -163,7 +163,7 @@ function editHeightmap() { // restore initial layers //viewbox.select("#heights").remove(); - document.getElementById("heights").remove(); + byId("heights").remove(); turnButtonOff("toggleHeight"); document .getElementById("mapLayers") @@ -487,8 +487,8 @@ function editHeightmap() { function updateStatistics() { const landCells = grid.cells.h.reduce((s, h) => (h >= 20 ? s + 1 : s)); - landmassCounter.innerHTML = /* html */ `${landCells} (${rn((landCells / grid.cells.i.length) * 100)}%)`; - landmassAverage.innerHTML = rn(d3.mean(grid.cells.h)); + byId("landmassCounter").innerText = `${landCells} (${rn((landCells / grid.cells.i.length) * 100)}%)`; + byId("landmassAverage").innerText = rn(d3.mean(grid.cells.h)); } function updateHistory(noStat) { @@ -501,8 +501,8 @@ function editHeightmap() { redo.disabled = templateRedo.disabled = true; if (!noStat) { updateStatistics(); - if (document.getElementById("preview")) drawHeightmapPreview(); // update heightmap preview if opened - if (document.getElementById("canvas3d")) ThreeD.redraw(); // update 3d heightmap preview if opened + if (byId("preview")) drawHeightmapPreview(); // update heightmap preview if opened + if (byId("canvas3d")) ThreeD.redraw(); // update 3d heightmap preview if opened } } @@ -516,8 +516,8 @@ function editHeightmap() { mockHeightmap(); updateStatistics(); - if (document.getElementById("preview")) drawHeightmapPreview(); // update heightmap preview if opened - if (document.getElementById("canvas3d")) ThreeD.redraw(); // update 3d heightmap preview if opened + if (byId("preview")) drawHeightmapPreview(); // update heightmap preview if opened + if (byId("canvas3d")) ThreeD.redraw(); // update 3d heightmap preview if opened } // restart edits from 1st step @@ -543,31 +543,31 @@ function editHeightmap() { modules.openBrushesPanel = true; // add listeners - document.getElementById("brushesButtons").addEventListener("click", e => toggleBrushMode(e)); - document.getElementById("changeOnlyLand").addEventListener("click", e => changeOnlyLandClick(e)); - document.getElementById("undo").addEventListener("click", () => restoreHistory(edits.n - 1)); - document.getElementById("redo").addEventListener("click", () => restoreHistory(edits.n + 1)); - document.getElementById("rescaleShow").addEventListener("click", () => { - document.getElementById("modifyButtons").style.display = "none"; - document.getElementById("rescaleSection").style.display = "block"; + byId("brushesButtons").on("click", e => toggleBrushMode(e)); + byId("changeOnlyLand").on("click", e => changeOnlyLandClick(e)); + byId("undo").on("click", () => restoreHistory(edits.n - 1)); + byId("redo").on("click", () => restoreHistory(edits.n + 1)); + byId("rescaleShow").on("click", () => { + byId("modifyButtons").style.display = "none"; + byId("rescaleSection").style.display = "block"; }); - document.getElementById("rescaleHide").addEventListener("click", () => { - document.getElementById("modifyButtons").style.display = "block"; - document.getElementById("rescaleSection").style.display = "none"; + byId("rescaleHide").on("click", () => { + byId("modifyButtons").style.display = "block"; + byId("rescaleSection").style.display = "none"; }); - document.getElementById("rescaler").addEventListener("change", e => rescale(e.target.valueAsNumber)); - document.getElementById("rescaleCondShow").addEventListener("click", () => { - document.getElementById("modifyButtons").style.display = "none"; - document.getElementById("rescaleCondSection").style.display = "block"; + byId("rescaler").on("change", e => rescale(e.target.valueAsNumber)); + byId("rescaleCondShow").on("click", () => { + byId("modifyButtons").style.display = "none"; + byId("rescaleCondSection").style.display = "block"; }); - document.getElementById("rescaleCondHide").addEventListener("click", () => { - document.getElementById("modifyButtons").style.display = "block"; - document.getElementById("rescaleCondSection").style.display = "none"; + byId("rescaleCondHide").on("click", () => { + byId("modifyButtons").style.display = "block"; + byId("rescaleCondSection").style.display = "none"; }); - document.getElementById("rescaleExecute").addEventListener("click", rescaleWithCondition); - document.getElementById("smoothHeights").addEventListener("click", smoothAllHeights); - document.getElementById("disruptHeights").addEventListener("click", disruptAllHeights); - document.getElementById("brushClear").addEventListener("click", startFromScratch); + byId("rescaleExecute").on("click", rescaleWithCondition); + byId("smoothHeights").on("click", smoothAllHeights); + byId("disruptHeights").on("click", disruptAllHeights); + byId("brushClear").on("click", startFromScratch); function exitBrushMode() { const pressed = document.querySelector("#brushesButtons > button.pressed"); @@ -576,7 +576,7 @@ function editHeightmap() { viewbox.style("cursor", "default").on(".drag", null); removeCircle(); - document.getElementById("brushesSliders").style.display = "none"; + byId("brushesSliders").style.display = "none"; } const dragBrushThrottled = throttle(dragBrush, 100); @@ -586,7 +586,7 @@ function editHeightmap() { return; } exitBrushMode(); - document.getElementById("brushesSliders").style.display = "block"; + byId("brushesSliders").style.display = "block"; e.target.classList.add("pressed"); viewbox.style("cursor", "crosshair").call(d3.drag().on("start", dragBrushThrottled)); } @@ -613,9 +613,7 @@ function editHeightmap() { const power = brushPower.valueAsNumber; const interpolate = d3.interpolateRound(power, 1); const land = changeOnlyLand.checked; - function lim(v) { - return minmax(v, land ? 20 : 0, 100); - } + const lim = v => minmax(v, land ? 20 : 0, 100); const h = grid.cells.h; const brush = document.querySelector("#brushesButtons > button.pressed").id; @@ -644,21 +642,15 @@ function editHeightmap() { const land = changeOnlyLand.checked; grid.cells.h = grid.cells.h.map(h => (land && (h < 20 || h + v < 20) ? h : lim(h + v))); updateHeightmap(); - document.getElementById("rescaler").value = 0; + byId("rescaler").value = 0; } function rescaleWithCondition() { const range = rescaleLower.value + "-" + rescaleHigher.value; const operator = conditionSign.value; const operand = rescaleModifier.valueAsNumber; - if (Number.isNaN(operand)) { - tip("Operand should be a number", false, "error"); - return; - } - if ((operator === "add" || operator === "subtract") && !Number.isInteger(operand)) { - tip("Operand should be an integer", false, "error"); - return; - } + if (Number.isNaN(operand)) return tip("Operand should be a number", false, "error"); + if ((operator === "add" || operator === "subtract") && !Number.isInteger(operand)) return tip("Operand should be an integer", false, "error"); if (operator === "multiply") HeightmapGenerator.modify(range, 0, operand, 0); else if (operator === "divide") HeightmapGenerator.modify(range, 0, 1 / operand, 0); @@ -680,15 +672,10 @@ function editHeightmap() { } function startFromScratch() { - if (changeOnlyLand.checked) { - tip("Not allowed when 'Change only land cells' mode is set", false, "error"); - return; - } + if (changeOnlyLand.checked) return tip("Not allowed when 'Change only land cells' mode is set", false, "error"); const someHeights = grid.cells.h.some(h => h); - if (!someHeights) { - tip("Heightmap is already cleared, please do not click twice if not required", false, "error"); - return; - } + if (!someHeights) return tip("Heightmap is already cleared, please do not click twice if not required", false, "error"); + grid.cells.h = new Uint8Array(grid.cells.i.length); viewbox.select("#heights").selectAll("*").remove(); updateHistory(); @@ -697,7 +684,7 @@ function editHeightmap() { function openTemplateEditor() { if ($("#templateEditor").is(":visible")) return; - const body = document.getElementById("templateBody"); + const $body = byId("templateBody"); $("#templateEditor").dialog({ title: "Template Editor", @@ -713,13 +700,13 @@ function editHeightmap() { $("#templateBody").sortable({items: "> div", handle: ".icon-resize-vertical", containment: "#templateBody", axis: "y"}); // add listeners - body.addEventListener("click", function (ev) { + $body.on("click", function (ev) { const el = ev.target; if (el.classList.contains("icon-check")) { el.classList.remove("icon-check"); el.classList.add("icon-check-empty"); el.parentElement.style.opacity = 0.5; - body.dataset.changed = 1; + $body.dataset.changed = 1; return; } if (el.classList.contains("icon-check-empty")) { @@ -734,71 +721,146 @@ function editHeightmap() { } }); - document.getElementById("templateTools").addEventListener("click", e => addStepOnClick(e)); - document.getElementById("templateSelect").addEventListener("change", e => selectTemplate(e)); - document.getElementById("templateRun").addEventListener("click", executeTemplate); - document.getElementById("templateSave").addEventListener("click", downloadTemplate); - document.getElementById("templateLoad").addEventListener("click", () => templateToLoad.click()); - document.getElementById("templateToLoad").addEventListener("change", function () { + byId("templateEditor").on("keypress", event => { + if (event.key === "Enter") { + event.preventDefault(); + executeTemplate(); + } + }); + + byId("templateTools").on("click", addStepOnClick); + byId("templateSelect").on("change", selectTemplate); + byId("templateRun").on("click", executeTemplate); + byId("templateSave").on("click", downloadTemplate); + byId("templateLoad").on("click", () => templateToLoad.click()); + byId("templateToLoad").on("change", function () { uploadFile(this, uploadTemplate); }); function addStepOnClick(e) { if (e.target.tagName !== "BUTTON") return; - const type = e.target.id.replace("template", ""); - document.getElementById("templateBody").dataset.changed = 1; + const type = e.target.dataset.type; + byId("templateBody").dataset.changed = 1; addStep(type); } function addStep(type, count, dist, arg4, arg5) { - const body = document.getElementById("templateBody"); - body.insertAdjacentHTML("beforeend", getStepHTML(type, count, dist, arg4, arg5)); - const elDist = body.querySelector("div:last-child").querySelector(".templateDist"); - if (elDist) elDist.addEventListener("change", setRange); - if (dist && elDist && elDist.tagName === "SELECT") { - for (const o of elDist.options) { - if (o.value === dist) elDist.value = dist; + const $body = byId("templateBody"); + $body.insertAdjacentHTML("beforeend", getStepHTML(type, count, dist, arg4, arg5)); + + const $elDist = $body.querySelector("div:last-child > span > .templateDist"); + if ($elDist) $elDist.on("change", setRange); + + if (dist && $elDist && $elDist.tagName === "SELECT") { + for (const option of $elDist.options) { + if (option.value === dist) $elDist.value = dist; } - if (elDist.value !== dist) { + if ($elDist.value !== dist) { const opt = document.createElement("option"); opt.value = opt.innerHTML = dist; - elDist.add(opt); - elDist.value = dist; + $elDist.add(opt); + $elDist.value = dist; } } } function getStepHTML(type, count, arg3, arg4, arg5) { - const Trash = ``; - const Hide = `
`; - const Reorder = ``; - const common = `
${Hide}
${type}
${Trash}${Reorder}`; + const Trash = /* html */ ``; + const Hide = /* html */ `
`; + const Reorder = /* html */ ``; + const common = /* html */ `
${Hide}
${type}
${Trash}${Reorder}`; - const TempY = `y:`; - const TempX = `x:`; - const Height = `h:`; - const Count = `n:`; - const blob = `${common}${TempY}${TempX}${Height}${Count}
`; + const TempY = /* html */ `y: + + `; + + const TempX = /* html */ `x: + + `; + + const Height = /* html */ `h: + + `; + + const Count = /* html */ `n: + + `; + + if (type === "Hill" || type === "Pit" || type === "Range" || type === "Trough") return /* html */ `${common}${TempY}${TempX}${Height}${Count}
`; - if (type === "Hill" || type === "Pit" || type === "Range" || type === "Trough") return blob; if (type === "Strait") - return `${common}d:w:`; + return /* html */ `${common} + d: + + + w: + + + `; + + if (type === "Invert") + return /* html */ `${common} + by: + + + n: + + + `; + + if (type === "Mask") + return /* html */ `${common} + f: + + + `; + if (type === "Add") - return `${common}to:v:`; + return /* html */ `${common} + to: + + + v: + + + `; + if (type === "Multiply") - return `${common}to:v:`; + return /* html */ `${common} + to: + + + v: + + + `; + if (type === "Smooth") - return `${common}f:`; + return /* html */ `${common} + f: + + + `; } function setRange(event) { @@ -813,7 +875,7 @@ function editHeightmap() { } function selectTemplate(e) { - const body = document.getElementById("templateBody"); + const body = byId("templateBody"); const steps = body.querySelectorAll("div").length; const changed = +body.getAttribute("data-changed"); const template = e.target.value; @@ -839,7 +901,7 @@ function editHeightmap() { } function changeTemplate(template) { - const body = document.getElementById("templateBody"); + const body = byId("templateBody"); body.setAttribute("data-changed", 0); body.innerHTML = ""; @@ -856,43 +918,47 @@ function editHeightmap() { } function executeTemplate() { - const body = document.getElementById("templateBody"); - const steps = body.querySelectorAll("#templateBody > div"); + const steps = byId("templateBody").querySelectorAll("#templateBody > div"); if (!steps.length) return; - const {addHill, addPit, addRange, addTrough, addStrait, modify, smooth} = HeightmapGenerator; grid.cells.h = new Uint8Array(grid.cells.i.length); // clean all heights + const seed = byId("templateSeed").value; + if (seed) Math.random = aleaPRNG(seed); + restartHistory(); + for (const step of steps) { if (step.style.opacity === "0.5") continue; - const type = step.dataset.type; const count = step.querySelector(".templateCount")?.value || ""; const height = step.querySelector(".templateHeight")?.value || ""; const dist = step.querySelector(".templateDist")?.value || null; const x = step.querySelector(".templateX")?.value || null; const y = step.querySelector(".templateY")?.value || null; + const type = step.dataset.type; - if (type === "Hill") addHill(count, height, x, y); - else if (type === "Pit") addPit(count, height, x, y); - else if (type === "Range") addRange(count, height, x, y); - else if (type === "Trough") addTrough(count, height, x, y); - else if (type === "Strait") addStrait(count, dist); - else if (type === "Add") modify(dist, +count, 1); - else if (type === "Multiply") modify(dist, 0, +count); - else if (type === "Smooth") smooth(+count); + if (type === "Hill") HeightmapGenerator.addHill(count, height, x, y); + else if (type === "Pit") HeightmapGenerator.addPit(count, height, x, y); + else if (type === "Range") HeightmapGenerator.addRange(count, height, x, y); + else if (type === "Trough") HeightmapGenerator.addTrough(count, height, x, y); + else if (type === "Strait") HeightmapGenerator.addStrait(count, dist); + else if (type === "Mask") HeightmapGenerator.mask(+count); + else if (type === "Invert") HeightmapGenerator.invert(+count, dist); + else if (type === "Add") HeightmapGenerator.modify(dist, +count, 1); + else if (type === "Multiply") HeightmapGenerator.modify(dist, 0, +count); + else if (type === "Smooth") HeightmapGenerator.smooth(+count); - updateHistory("noStat"); // update history every step + updateHistory("noStat"); // update history on every step } updateStatistics(); mockHeightmap(); - if (document.getElementById("preview")) drawHeightmapPreview(); // update heightmap preview if opened - if (document.getElementById("canvas3d")) ThreeD.redraw(); // update 3d heightmap preview if opened + if (byId("preview")) drawHeightmapPreview(); // update heightmap preview if opened + if (byId("canvas3d")) ThreeD.redraw(); // update 3d heightmap preview if opened } function downloadTemplate() { - const body = document.getElementById("templateBody"); + const body = byId("templateBody"); body.dataset.changed = 0; const steps = body.querySelectorAll("#templateBody > div"); if (!steps.length) return; @@ -977,18 +1043,18 @@ function editHeightmap() { })(); // add listeners - document.getElementById("convertImageLoad").addEventListener("click", () => imageToLoad.click()); - document.getElementById("imageToLoad").addEventListener("change", loadImage); - document.getElementById("convertAutoLum").addEventListener("click", () => autoAssing("lum")); - document.getElementById("convertAutoHue").addEventListener("click", () => autoAssing("hue")); - document.getElementById("convertAutoFMG").addEventListener("click", () => autoAssing("scheme")); - document.getElementById("convertColorsButton").addEventListener("click", setConvertColorsNumber); - document.getElementById("convertComplete").addEventListener("click", applyConversion); - document.getElementById("convertCancel").addEventListener("click", cancelConversion); - document.getElementById("convertOverlay").addEventListener("input", function () { + byId("convertImageLoad").on("click", () => imageToLoad.click()); + byId("imageToLoad").on("change", loadImage); + byId("convertAutoLum").on("click", () => autoAssing("lum")); + byId("convertAutoHue").on("click", () => autoAssing("hue")); + byId("convertAutoFMG").on("click", () => autoAssing("scheme")); + byId("convertColorsButton").on("click", setConvertColorsNumber); + byId("convertComplete").on("click", applyConversion); + byId("convertCancel").on("click", cancelConversion); + byId("convertOverlay").on("input", function () { setOverlayOpacity(this.value); }); - document.getElementById("convertOverlayNumber").addEventListener("input", function () { + byId("convertOverlayNumber").on("input", function () { setOverlayOpacity(this.value); }); @@ -1012,7 +1078,7 @@ function editHeightmap() { document.body.appendChild(img); img.onload = function () { - const ctx = document.getElementById("canvas").getContext("2d"); + const ctx = byId("canvas").getContext("2d"); ctx.drawImage(img, 0, 0, graphWidth, graphHeight); heightsFromImage(+convertColors.value); resetZoom(); @@ -1023,7 +1089,7 @@ function editHeightmap() { } function heightsFromImage(count) { - const sourceImage = document.getElementById("canvas"); + const sourceImage = byId("canvas"); const sampleCanvas = document.createElement("canvas"); sampleCanvas.width = grid.cellsX; sampleCanvas.height = grid.cellsY; @@ -1062,7 +1128,7 @@ function editHeightmap() { .attr("class", "color-div") .on("click", colorClicked); - document.getElementById("colorsUnassignedNumber").innerHTML = colors.length; + byId("colorsUnassignedNumber").innerHTML = colors.length; } function mapClicked() { @@ -1119,8 +1185,8 @@ function editHeightmap() { colorsAssigned.appendChild(selectedColor); colorsAssigned.style.display = "block"; - document.getElementById("colorsUnassignedNumber").innerHTML = colorsUnassigned.childElementCount - 2; - document.getElementById("colorsAssignedNumber").innerHTML = colorsAssigned.childElementCount - 2; + byId("colorsUnassignedNumber").innerHTML = colorsUnassigned.childElementCount - 2; + byId("colorsAssignedNumber").innerHTML = colorsAssigned.childElementCount - 2; } } @@ -1187,7 +1253,7 @@ function editHeightmap() { colorsAssigned.style.display = "block"; colorsUnassigned.style.display = "none"; - document.getElementById("colorsAssignedNumber").innerHTML = colorsAssigned.childElementCount - 2; + byId("colorsAssignedNumber").innerHTML = colorsAssigned.childElementCount - 2; } function setConvertColorsNumber() { @@ -1203,7 +1269,7 @@ function editHeightmap() { function setOverlayOpacity(v) { convertOverlay.value = convertOverlayNumber.value = v; - document.getElementById("canvas").style.opacity = v; + byId("canvas").style.opacity = v; } function applyConversion() { @@ -1230,10 +1296,10 @@ function editHeightmap() { } function restoreImageConverterState() { - const canvas = document.getElementById("canvas"); + const canvas = byId("canvas"); if (canvas) canvas.remove(); - const image = document.getElementById("imageToConvert"); + const image = byId("imageToConvert"); if (image) image.remove(); d3.select("#imageConverter").selectAll("div.color-div").remove(); @@ -1275,8 +1341,8 @@ function editHeightmap() { } function toggleHeightmapPreview() { - if (document.getElementById("preview")) { - document.getElementById("preview").remove(); + if (byId("preview")) { + byId("preview").remove(); return; } const preview = document.createElement("canvas"); @@ -1284,13 +1350,13 @@ function editHeightmap() { preview.width = grid.cellsX; preview.height = grid.cellsY; document.body.insertBefore(preview, optionsContainer); - preview.addEventListener("mouseover", () => tip("Heightmap preview. Click to download a screen-sized image")); - preview.addEventListener("click", downloadPreview); + preview.on("mouseover", () => tip("Heightmap preview. Click to download a screen-sized image")); + preview.on("click", downloadPreview); drawHeightmapPreview(); } function drawHeightmapPreview() { - const ctx = document.getElementById("preview").getContext("2d"); + const ctx = byId("preview").getContext("2d"); const imageData = ctx.createImageData(grid.cellsX, grid.cellsY); grid.cells.h.forEach((height, i) => { @@ -1306,7 +1372,7 @@ function editHeightmap() { } function downloadPreview() { - const preview = document.getElementById("preview"); + const preview = byId("preview"); const dataURL = preview.toDataURL("image/png"); const img = new Image(); diff --git a/versioning.js b/versioning.js index ee337422..54c53146 100644 --- a/versioning.js +++ b/versioning.js @@ -1,7 +1,7 @@ "use strict"; // version and caching control -const version = "1.82.05"; // generator version, update each time +const version = "1.83.0"; // generator version, update each time { document.title += " v" + version; @@ -28,6 +28,8 @@ const version = "1.82.05"; // generator version, update each time

Join our Discord server and Reddit community to ask questions, share maps, discuss the Generator and Worlbuilding, report bugs and propose new features.