heightmap editor - insulate feature

This commit is contained in:
Azgaar 2022-05-21 01:50:12 +03:00
parent 118306dca2
commit cf92a65ea1
4 changed files with 184 additions and 141 deletions

View file

@ -2951,14 +2951,15 @@
</select> </select>
</div> </div>
<div id="templateTools"> <div id="templateTools">
<button id="templateHill" data-tip="Hill: small blob" class="noicon">H</button> <button data-type="Hill" data-tip="Hill: small blob" class="noicon">H</button>
<button id="templatePit" data-tip="Pit: round depression" class="noicon">P</button> <button data-type="Pit" data-tip="Pit: round depression" class="noicon">P</button>
<button id="templateRange" data-tip="Range: elongated elevation" class="noicon">R</button> <button data-type="Range" data-tip="Range: elongated elevation" class="noicon">R</button>
<button id="templateTrough" data-tip="Trough: elongated depression" class="noicon">T</button> <button data-type="Trough" data-tip="Trough: elongated depression" class="noicon">T</button>
<button id="templateStrait" data-tip="Strait: centered vertical or horizontal depression" class="noicon">S</button> <button data-type="Strait" data-tip="Strait: centered vertical or horizontal depression" class="noicon">S</button>
<button id="templateAdd" data-tip="Add or subtract value from all heights in range" class="noicon">+</button> <button data-type="Insulate" data-tip="Insulate: lower cells near edges" class="noicon">I</button>
<button id="templateMultiply" data-tip="Multiply all heights in range by factor" class="noicon">*</button> <button data-type="Add" data-tip="Add or subtract value from all heights in range" class="noicon">+</button>
<button id="templateSmooth" data-tip="Smooth the map replacing cell heights by an average values of its neighbors" class="noicon">~</button> <button data-type="Multiply" data-tip="Multiply all heights in range by factor" class="noicon">*</button>
<button data-type="Smooth" data-tip="Smooth the map replacing cell heights by an average values of its neighbors" class="noicon">~</button>
</div> </div>
<div id="templateBody" data-changed="0" class="table" style="padding: 2px 0"> <div id="templateBody" data-changed="0" class="table" style="padding: 2px 0">
<div data-type="Hill"> <div data-type="Hill">
@ -6110,7 +6111,7 @@
<script src="modules/voronoi.js"></script> <script src="modules/voronoi.js"></script>
<script src="modules/heightmap-templates.js"></script> <script src="modules/heightmap-templates.js"></script>
<script src="modules/heightmap-generator.js"></script> <script src="modules/heightmap-generator.js?v=21052020"></script>
<script src="modules/ocean-layers.js"></script> <script src="modules/ocean-layers.js"></script>
<script src="modules/river-generator.js"></script> <script src="modules/river-generator.js"></script>
<script src="modules/lakes.js"></script> <script src="modules/lakes.js"></script>
@ -6140,7 +6141,7 @@
<script defer src="modules/ui/editors.js?v=18052022"></script> <script defer src="modules/ui/editors.js?v=18052022"></script>
<script defer src="modules/ui/tools.js"></script> <script defer src="modules/ui/tools.js"></script>
<script defer src="modules/ui/world-configurator.js"></script> <script defer src="modules/ui/world-configurator.js"></script>
<script defer src="modules/ui/heightmap-editor.js"></script> <script defer src="modules/ui/heightmap-editor.js?v=21052020"></script>
<script defer src="modules/ui/provinces-editor.js?v=19052022"></script> <script defer src="modules/ui/provinces-editor.js?v=19052022"></script>
<script defer src="modules/ui/biomes-editor.js?v=18052022"></script> <script defer src="modules/ui/biomes-editor.js?v=18052022"></script>
<script defer src="modules/ui/namesbase-editor.js"></script> <script defer src="modules/ui/namesbase-editor.js"></script>

View file

@ -26,7 +26,7 @@ window.HeightmapGenerator = (function () {
// load heightmap into image and render to canvas // load heightmap into image and render to canvas
const img = new Image(); const img = new Image();
img.src = `./heightmaps/${input.value}.png`; img.src = `./heightmaps/${input.value}.png`;
img.onload = function () { img.onload = () => {
ctx.drawImage(img, 0, 0, cellsX, cellsY); ctx.drawImage(img, 0, 0, cellsX, cellsY);
const imageData = ctx.getImageData(0, 0, cellsX, cellsY); const imageData = ctx.getImageData(0, 0, cellsX, cellsY);
assignColorsToHeight(imageData.data); assignColorsToHeight(imageData.data);
@ -61,6 +61,7 @@ window.HeightmapGenerator = (function () {
if (a1 === "Range") return addRange(a2, a3, a4, a5); if (a1 === "Range") return addRange(a2, a3, a4, a5);
if (a1 === "Trough") return addTrough(a2, a3, a4, a5); if (a1 === "Trough") return addTrough(a2, a3, a4, a5);
if (a1 === "Strait") return addStrait(a2, a3); if (a1 === "Strait") return addStrait(a2, a3);
if (a1 === "Insulate") return insulate(a2);
if (a1 === "Add") return modify(a3, +a2, 1); if (a1 === "Add") return modify(a3, +a2, 1);
if (a1 === "Multiply") return modify(a3, 0, +a2); if (a1 === "Multiply") return modify(a3, 0, +a2);
if (a1 === "Smooth") return smooth(a2); if (a1 === "Smooth") return smooth(a2);
@ -100,7 +101,7 @@ window.HeightmapGenerator = (function () {
if (cells === 100000) return 0.93; if (cells === 100000) return 0.93;
} }
const addHill = function (count, height, rangeX, rangeY) { const addHill = (count, height, rangeX, rangeY) => {
count = getNumberInRange(count); count = getNumberInRange(count);
const power = getBlobPower(); const power = getBlobPower();
while (count > 0) { 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); count = getNumberInRange(count);
while (count > 0) { while (count > 0) {
addOnePit(); addOnePit();
@ -173,7 +174,7 @@ window.HeightmapGenerator = (function () {
} }
}; };
const addRange = function (count, height, rangeX, rangeY) { const addRange = (count, height, rangeX, rangeY) => {
count = getNumberInRange(count); count = getNumberInRange(count);
const power = getLinePower(); const power = getLinePower();
while (count > 0) { 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); count = getNumberInRange(count);
const power = getLinePower(); const power = getLinePower();
while (count > 0) { 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); width = Math.min(getNumberInRange(width), grid.cellsX / 3);
if (width < 1 && P(width)) return; if (width < 1 && P(width)) return;
const used = new Uint8Array(cells.h.length); const used = new Uint8Array(cells.h.length);
@ -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 min = range === "land" ? 20 : range === "all" ? 0 : +range.split("-")[0];
const max = range === "land" || range === "all" ? 100 : +range.split("-")[1]; const max = range === "land" || range === "all" ? 100 : +range.split("-")[1];
const isLand = min === 20; const isLand = min === 20;
@ -422,14 +423,26 @@ window.HeightmapGenerator = (function () {
}); });
}; };
const smooth = function (fr = 2, add = 0) { const smooth = (fr = 2, add = 0) => {
cells.h = cells.h.map((h, i) => { cells.h = cells.h.map((h, i) => {
const a = [h]; const a = [h];
cells.c[i].forEach(c => a.push(cells.h[c])); 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); return lim((h * (fr - 1) + d3.mean(a) + add) / fr);
}); });
}; };
const insulate = (fr = 2) => {
const power = fr * 2;
cells.h = cells.h.map((h, i) => {
const [x, y] = p[i];
const nx = (2 * x) / graphWidth - 1;
const ny = (2 * y) / graphHeight - 1;
const distance = (1 - nx ** power) * (1 - ny ** power); // 1 is center, 0 is edge
return h * distance;
});
};
function getPointInRange(range, length) { function getPointInRange(range, length) {
if (typeof range !== "string") { if (typeof range !== "string") {
ERROR && console.error("Range should be a string"); ERROR && console.error("Range should be a string");
@ -449,5 +462,5 @@ window.HeightmapGenerator = (function () {
} }
} }
return {generate, addHill, addRange, addTrough, addStrait, addPit, smooth, modify}; return {generate, addHill, addRange, addTrough, addStrait, addPit, smooth, modify, insulate};
})(); })();

View file

@ -119,7 +119,6 @@ window.HeightmapTemplates = (function () {
Hill 3-4 60-85 20-80 0-5 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`;
const oldWorld = `Hill 4-6 20-40 15-85 30-45 const oldWorld = `Hill 4-6 20-40 15-85 30-45
Hill 3-7 20-40 15-85 55-70 Hill 3-7 20-40 15-85 55-70
Strait 2-7 vertical 0 0 Strait 2-7 vertical 0 0
@ -170,5 +169,20 @@ window.HeightmapTemplates = (function () {
Strait 3-7 vertical 0 0 Strait 3-7 vertical 0 0
Trough 1-2 40-50 45-65 45-65`; Trough 1-2 40-50 45-65 45-65`;
return {volcano, highIsland, lowIsland, continents, archipelago, atoll, mediterranean, peninsula, peninsula, pangea, isthmus, shattered, taklamakan, oldWorld}; return {
volcano,
highIsland,
lowIsland,
continents,
archipelago,
atoll,
mediterranean,
peninsula,
peninsula,
pangea,
isthmus,
shattered,
taklamakan,
oldWorld
};
})(); })();

View file

@ -34,19 +34,19 @@ function editHeightmap() {
modules.editHeightmap = true; modules.editHeightmap = true;
// add listeners // add listeners
document.getElementById("paintBrushes").addEventListener("click", openBrushesPanel); byId("paintBrushes").on("click", openBrushesPanel);
document.getElementById("applyTemplate").addEventListener("click", openTemplateEditor); byId("applyTemplate").on("click", openTemplateEditor);
document.getElementById("convertImage").addEventListener("click", openImageConverter); byId("convertImage").on("click", openImageConverter);
document.getElementById("heightmapPreview").addEventListener("click", toggleHeightmapPreview); byId("heightmapPreview").on("click", toggleHeightmapPreview);
document.getElementById("heightmap3DView").addEventListener("click", changeViewMode); byId("heightmap3DView").on("click", changeViewMode);
document.getElementById("finalizeHeightmap").addEventListener("click", finalizeHeightmap); byId("finalizeHeightmap").on("click", finalizeHeightmap);
document.getElementById("renderOcean").addEventListener("click", mockHeightmap); byId("renderOcean").on("click", mockHeightmap);
document.getElementById("templateUndo").addEventListener("click", () => restoreHistory(edits.n - 1)); byId("templateUndo").on("click", () => restoreHistory(edits.n - 1));
document.getElementById("templateRedo").addEventListener("click", () => restoreHistory(edits.n + 1)); byId("templateRedo").on("click", () => restoreHistory(edits.n + 1));
function enterHeightmapEditMode(type) { function enterHeightmapEditMode(type) {
editHeightmap.layers = Array.from(mapLayers.querySelectorAll("li:not(.buttonoff)")).map(node => node.id); // store layers preset 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; customization = 1;
closeDialogs(); closeDialogs();
@ -113,7 +113,7 @@ function editHeightmap() {
if (tooltip.dataset.main) showMainTip(); if (tooltip.dataset.main) showMainTip();
// move radius circle if drag mode is active // 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; if (!pressed) return;
moveCircle(p[0], p[1], brushRadius.valueAsNumber, "#333"); moveCircle(p[0], p[1], brushRadius.valueAsNumber, "#333");
} }
@ -137,7 +137,7 @@ function editHeightmap() {
function finalizeHeightmap() { function finalizeHeightmap() {
if (viewbox.select("#heights").selectAll("*").size() < 200) 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"); 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 delete window.edits; // remove global variable
redo.disabled = templateRedo.disabled = true; redo.disabled = templateRedo.disabled = true;
@ -145,7 +145,7 @@ function editHeightmap() {
customization = 0; customization = 0;
customizationMenu.style.display = "none"; 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; layersPreset.disabled = false;
exitCustomization.style.display = "none"; // hide finalize button exitCustomization.style.display = "none"; // hide finalize button
restoreDefaultEvents(); restoreDefaultEvents();
@ -153,8 +153,8 @@ function editHeightmap() {
closeDialogs(); closeDialogs();
resetZoom(); resetZoom();
if (document.getElementById("preview")) document.getElementById("preview").remove(); if (byId("preview")) byId("preview").remove();
if (document.getElementById("canvas3d")) enterStandardView(); if (byId("canvas3d")) enterStandardView();
const mode = heightmapEditMode.innerHTML; const mode = heightmapEditMode.innerHTML;
if (mode === "erase") regenerateErasedData(); if (mode === "erase") regenerateErasedData();
@ -163,7 +163,7 @@ function editHeightmap() {
// restore initial layers // restore initial layers
//viewbox.select("#heights").remove(); //viewbox.select("#heights").remove();
document.getElementById("heights").remove(); byId("heights").remove();
turnButtonOff("toggleHeight"); turnButtonOff("toggleHeight");
document document
.getElementById("mapLayers") .getElementById("mapLayers")
@ -501,8 +501,8 @@ function editHeightmap() {
redo.disabled = templateRedo.disabled = true; redo.disabled = templateRedo.disabled = true;
if (!noStat) { if (!noStat) {
updateStatistics(); updateStatistics();
if (document.getElementById("preview")) drawHeightmapPreview(); // update heightmap preview if opened if (byId("preview")) drawHeightmapPreview(); // update heightmap preview if opened
if (document.getElementById("canvas3d")) ThreeD.redraw(); // update 3d heightmap preview if opened if (byId("canvas3d")) ThreeD.redraw(); // update 3d heightmap preview if opened
} }
} }
@ -516,8 +516,8 @@ function editHeightmap() {
mockHeightmap(); mockHeightmap();
updateStatistics(); updateStatistics();
if (document.getElementById("preview")) drawHeightmapPreview(); // update heightmap preview if opened if (byId("preview")) drawHeightmapPreview(); // update heightmap preview if opened
if (document.getElementById("canvas3d")) ThreeD.redraw(); // update 3d heightmap preview if opened if (byId("canvas3d")) ThreeD.redraw(); // update 3d heightmap preview if opened
} }
// restart edits from 1st step // restart edits from 1st step
@ -543,31 +543,31 @@ function editHeightmap() {
modules.openBrushesPanel = true; modules.openBrushesPanel = true;
// add listeners // add listeners
document.getElementById("brushesButtons").addEventListener("click", e => toggleBrushMode(e)); byId("brushesButtons").on("click", e => toggleBrushMode(e));
document.getElementById("changeOnlyLand").addEventListener("click", e => changeOnlyLandClick(e)); byId("changeOnlyLand").on("click", e => changeOnlyLandClick(e));
document.getElementById("undo").addEventListener("click", () => restoreHistory(edits.n - 1)); byId("undo").on("click", () => restoreHistory(edits.n - 1));
document.getElementById("redo").addEventListener("click", () => restoreHistory(edits.n + 1)); byId("redo").on("click", () => restoreHistory(edits.n + 1));
document.getElementById("rescaleShow").addEventListener("click", () => { byId("rescaleShow").on("click", () => {
document.getElementById("modifyButtons").style.display = "none"; byId("modifyButtons").style.display = "none";
document.getElementById("rescaleSection").style.display = "block"; byId("rescaleSection").style.display = "block";
}); });
document.getElementById("rescaleHide").addEventListener("click", () => { byId("rescaleHide").on("click", () => {
document.getElementById("modifyButtons").style.display = "block"; byId("modifyButtons").style.display = "block";
document.getElementById("rescaleSection").style.display = "none"; byId("rescaleSection").style.display = "none";
}); });
document.getElementById("rescaler").addEventListener("change", e => rescale(e.target.valueAsNumber)); byId("rescaler").on("change", e => rescale(e.target.valueAsNumber));
document.getElementById("rescaleCondShow").addEventListener("click", () => { byId("rescaleCondShow").on("click", () => {
document.getElementById("modifyButtons").style.display = "none"; byId("modifyButtons").style.display = "none";
document.getElementById("rescaleCondSection").style.display = "block"; byId("rescaleCondSection").style.display = "block";
}); });
document.getElementById("rescaleCondHide").addEventListener("click", () => { byId("rescaleCondHide").on("click", () => {
document.getElementById("modifyButtons").style.display = "block"; byId("modifyButtons").style.display = "block";
document.getElementById("rescaleCondSection").style.display = "none"; byId("rescaleCondSection").style.display = "none";
}); });
document.getElementById("rescaleExecute").addEventListener("click", rescaleWithCondition); byId("rescaleExecute").on("click", rescaleWithCondition);
document.getElementById("smoothHeights").addEventListener("click", smoothAllHeights); byId("smoothHeights").on("click", smoothAllHeights);
document.getElementById("disruptHeights").addEventListener("click", disruptAllHeights); byId("disruptHeights").on("click", disruptAllHeights);
document.getElementById("brushClear").addEventListener("click", startFromScratch); byId("brushClear").on("click", startFromScratch);
function exitBrushMode() { function exitBrushMode() {
const pressed = document.querySelector("#brushesButtons > button.pressed"); const pressed = document.querySelector("#brushesButtons > button.pressed");
@ -576,7 +576,7 @@ function editHeightmap() {
viewbox.style("cursor", "default").on(".drag", null); viewbox.style("cursor", "default").on(".drag", null);
removeCircle(); removeCircle();
document.getElementById("brushesSliders").style.display = "none"; byId("brushesSliders").style.display = "none";
} }
const dragBrushThrottled = throttle(dragBrush, 100); const dragBrushThrottled = throttle(dragBrush, 100);
@ -586,7 +586,7 @@ function editHeightmap() {
return; return;
} }
exitBrushMode(); exitBrushMode();
document.getElementById("brushesSliders").style.display = "block"; byId("brushesSliders").style.display = "block";
e.target.classList.add("pressed"); e.target.classList.add("pressed");
viewbox.style("cursor", "crosshair").call(d3.drag().on("start", dragBrushThrottled)); viewbox.style("cursor", "crosshair").call(d3.drag().on("start", dragBrushThrottled));
} }
@ -644,21 +644,15 @@ function editHeightmap() {
const land = changeOnlyLand.checked; const land = changeOnlyLand.checked;
grid.cells.h = grid.cells.h.map(h => (land && (h < 20 || h + v < 20) ? h : lim(h + v))); grid.cells.h = grid.cells.h.map(h => (land && (h < 20 || h + v < 20) ? h : lim(h + v)));
updateHeightmap(); updateHeightmap();
document.getElementById("rescaler").value = 0; byId("rescaler").value = 0;
} }
function rescaleWithCondition() { function rescaleWithCondition() {
const range = rescaleLower.value + "-" + rescaleHigher.value; const range = rescaleLower.value + "-" + rescaleHigher.value;
const operator = conditionSign.value; const operator = conditionSign.value;
const operand = rescaleModifier.valueAsNumber; const operand = rescaleModifier.valueAsNumber;
if (Number.isNaN(operand)) { if (Number.isNaN(operand)) return tip("Operand should be a number", false, "error");
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");
return;
}
if ((operator === "add" || operator === "subtract") && !Number.isInteger(operand)) {
tip("Operand should be an integer", false, "error");
return;
}
if (operator === "multiply") HeightmapGenerator.modify(range, 0, operand, 0); if (operator === "multiply") HeightmapGenerator.modify(range, 0, operand, 0);
else if (operator === "divide") HeightmapGenerator.modify(range, 0, 1 / operand, 0); else if (operator === "divide") HeightmapGenerator.modify(range, 0, 1 / operand, 0);
@ -697,7 +691,7 @@ function editHeightmap() {
function openTemplateEditor() { function openTemplateEditor() {
if ($("#templateEditor").is(":visible")) return; if ($("#templateEditor").is(":visible")) return;
const body = document.getElementById("templateBody"); const body = byId("templateBody");
$("#templateEditor").dialog({ $("#templateEditor").dialog({
title: "Template Editor", title: "Template Editor",
@ -713,7 +707,7 @@ function editHeightmap() {
$("#templateBody").sortable({items: "> div", handle: ".icon-resize-vertical", containment: "#templateBody", axis: "y"}); $("#templateBody").sortable({items: "> div", handle: ".icon-resize-vertical", containment: "#templateBody", axis: "y"});
// add listeners // add listeners
body.addEventListener("click", function (ev) { body.on("click", function (ev) {
const el = ev.target; const el = ev.target;
if (el.classList.contains("icon-check")) { if (el.classList.contains("icon-check")) {
el.classList.remove("icon-check"); el.classList.remove("icon-check");
@ -734,27 +728,27 @@ function editHeightmap() {
} }
}); });
document.getElementById("templateTools").addEventListener("click", e => addStepOnClick(e)); byId("templateTools").on("click", addStepOnClick);
document.getElementById("templateSelect").addEventListener("change", e => selectTemplate(e)); byId("templateSelect").on("change", selectTemplate);
document.getElementById("templateRun").addEventListener("click", executeTemplate); byId("templateRun").on("click", executeTemplate);
document.getElementById("templateSave").addEventListener("click", downloadTemplate); byId("templateSave").on("click", downloadTemplate);
document.getElementById("templateLoad").addEventListener("click", () => templateToLoad.click()); byId("templateLoad").on("click", () => templateToLoad.click());
document.getElementById("templateToLoad").addEventListener("change", function () { byId("templateToLoad").on("change", function () {
uploadFile(this, uploadTemplate); uploadFile(this, uploadTemplate);
}); });
function addStepOnClick(e) { function addStepOnClick(e) {
if (e.target.tagName !== "BUTTON") return; if (e.target.tagName !== "BUTTON") return;
const type = e.target.id.replace("template", ""); const type = e.target.dataset.type;
document.getElementById("templateBody").dataset.changed = 1; byId("templateBody").dataset.changed = 1;
addStep(type); addStep(type);
} }
function addStep(type, count, dist, arg4, arg5) { function addStep(type, count, dist, arg4, arg5) {
const body = document.getElementById("templateBody"); const body = byId("templateBody");
body.insertAdjacentHTML("beforeend", getStepHTML(type, count, dist, arg4, arg5)); body.insertAdjacentHTML("beforeend", getStepHTML(type, count, dist, arg4, arg5));
const elDist = body.querySelector("div:last-child").querySelector(".templateDist"); const elDist = body.querySelector("div:last-child").querySelector(".templateDist");
if (elDist) elDist.addEventListener("change", setRange); if (elDist) elDist.on("change", setRange);
if (dist && elDist && elDist.tagName === "SELECT") { if (dist && elDist && elDist.tagName === "SELECT") {
for (const o of elDist.options) { for (const o of elDist.options) {
if (o.value === dist) elDist.value = dist; if (o.value === dist) elDist.value = dist;
@ -769,36 +763,56 @@ function editHeightmap() {
} }
function getStepHTML(type, count, arg3, arg4, arg5) { function getStepHTML(type, count, arg3, arg4, arg5) {
const Trash = `<i class="icon-trash-empty pointer" data-tip="Click to remove the step"></i>`; const Trash = /* html */ `<i class="icon-trash-empty pointer" data-tip="Click to remove the step"></i>`;
const Hide = `<div class="icon-check" data-tip="Click to skip the step"></div>`; const Hide = /* html */ `<div class="icon-check" data-tip="Click to skip the step"></div>`;
const Reorder = `<i class="icon-resize-vertical" data-tip="Drag to reorder"></i>`; const Reorder = /* html */ `<i class="icon-resize-vertical" data-tip="Drag to reorder"></i>`;
const common = `<div data-type="${type}">${Hide}<div style="width:4em">${type}</div>${Trash}${Reorder}`; const common = /* html */ `<div data-type="${type}">${Hide}<div style="width:4em">${type}</div>${Trash}${Reorder}`;
const TempY = `<span>y:<input class="templateY" data-tip="Placement range percentage along Y axis (minY-maxY)" value=${arg5 || "20-80"}></span>`; const TempY = /* html */ `<span>y:<input class="templateY" data-tip="Placement range percentage along Y axis (minY-maxY)"
const TempX = `<span>x:<input class="templateX" data-tip="Placement range percentage along X axis (minX-maxX)" value=${arg4 || "15-85"}></span>`; value=${arg5 || "20-80"}></span>`;
const Height = `<span>h:<input class="templateHeight" data-tip="Blob maximum height, use hyphen to get a random number in range" value=${
arg3 || "40-50" const TempX = /* html */ `<span>x:<input class="templateX" data-tip="Placement range percentage along X axis (minX-maxX)"
}></span>`; value=${arg4 || "15-85"}></span>`;
const Count = `<span>n:<input class="templateCount" data-tip="Blobs to add, use hyphen to get a random number in range" value=${count || "1-2"}></span>`;
const blob = `${common}${TempY}${TempX}${Height}${Count}</div>`; const Height = /* html */ `<span>h:<input class="templateHeight" data-tip="Blob maximum height, use hyphen to get a random number in range"
value=${arg3 || "40-50"}></span>`;
const Count = /* html */ `<span>n:<input class="templateCount" data-tip="Blobs to add, use hyphen to get a random number in range"
value=${count || "1-2"}></span>`;
if (type === "Hill" || type === "Pit" || type === "Range" || type === "Trough") return /* html */ `${common}${TempY}${TempX}${Height}${Count}</div>`;
if (type === "Hill" || type === "Pit" || type === "Range" || type === "Trough") return blob;
if (type === "Strait") if (type === "Strait")
return `${common}<span>d:<select class="templateDist" data-tip="Strait direction"><option value="vertical" selected>vertical</option><option value="horizontal">horizontal</option></select></span><span>w:<input class="templateCount" data-tip="Strait width, use hyphen to get a random number in range" value=${ return /* html */ `${common}<span>d:<select class="templateDist" data-tip="Strait direction">
count || "2-7" <option value="vertical" selected>vertical</option>
}></span></div>`; <option value="horizontal">horizontal</option>
</select></span>
<span>w:<input class="templateCount" data-tip="Strait width, use hyphen to get a random number in range" value=${count || "2-7"}></span></div>`;
if (type === "Insulate")
return /* html */ `${common}<span>f:<input class="templateCount" data-tip="Set insulation power. 1 - full insulation, 2 - half-insulation, etc." type="number" min=1 max=10
value=${count || 2}></span></div>`;
if (type === "Add") if (type === "Add")
return `${common}<span>to:<select class="templateDist" data-tip="Change only land or all cells"><option value="all" selected>all cells</option><option value="land">land only</option><option value="interval">interval</option></select></span><span>v:<input class="templateCount" data-tip="Add value to height of all cells (negative values are allowed)" type="number" value=${ return /* html */ `${common}<span>to:<select class="templateDist" data-tip="Change only land or all cells">
count || -10 <option value="all" selected>all cells</option>
} min=-100 max=100 step=1></span></div>`; <option value="land">land only</option>
<option value="interval">interval</option>
</select></span>
<span>v:<input class="templateCount" data-tip="Add value to height of all cells (negative values are allowed)"
type="number" value=${count || -10} min=-100 max=100 step=1></span></div>`;
if (type === "Multiply") if (type === "Multiply")
return `${common}<span>to:<select class="templateDist" data-tip="Change only land or all cells"><option value="all" selected>all cells</option><option value="land">land only</option><option value="interval">interval</option></select></span><span>v:<input class="templateCount" data-tip="Multiply all cells Height by the value" type="number" value=${ return /* html */ `${common}<span>to:<select class="templateDist" data-tip="Change only land or all cells">
count || 1.1 <option value="all" selected>all cells</option>
} min=0 max=10 step=.1></span></div>`; <option value="land">land only</option><option value="interval">interval</option>
</select></span>
<span>v:<input class="templateCount" data-tip="Multiply all cells Height by the value" type="number"
value=${count || 1.1} min=0 max=10 step=.1></span></div>`;
if (type === "Smooth") if (type === "Smooth")
return `${common}<span>f:<input class="templateCount" data-tip="Set smooth fraction. 1 - full smooth, 2 - half-smooth, etc." type="number" min=1 max=10 value=${ return /* html */ `${common}<span>f:<input class="templateCount" data-tip="Set smooth fraction. 1 - full smooth, 2 - half-smooth, etc." type="number" min=1 max=10
count || 2 value=${count || 2}></span></div>`;
}></span></div>`;
} }
function setRange(event) { function setRange(event) {
@ -813,7 +827,7 @@ function editHeightmap() {
} }
function selectTemplate(e) { function selectTemplate(e) {
const body = document.getElementById("templateBody"); const body = byId("templateBody");
const steps = body.querySelectorAll("div").length; const steps = body.querySelectorAll("div").length;
const changed = +body.getAttribute("data-changed"); const changed = +body.getAttribute("data-changed");
const template = e.target.value; const template = e.target.value;
@ -839,7 +853,7 @@ function editHeightmap() {
} }
function changeTemplate(template) { function changeTemplate(template) {
const body = document.getElementById("templateBody"); const body = byId("templateBody");
body.setAttribute("data-changed", 0); body.setAttribute("data-changed", 0);
body.innerHTML = ""; body.innerHTML = "";
@ -856,11 +870,11 @@ function editHeightmap() {
} }
function executeTemplate() { function executeTemplate() {
const body = document.getElementById("templateBody"); const body = byId("templateBody");
const steps = body.querySelectorAll("#templateBody > div"); const steps = body.querySelectorAll("#templateBody > div");
if (!steps.length) return; if (!steps.length) return;
const {addHill, addPit, addRange, addTrough, addStrait, modify, smooth} = HeightmapGenerator; const {addHill, addPit, addRange, addTrough, addStrait, modify, smooth, insulate} = HeightmapGenerator;
grid.cells.h = new Uint8Array(grid.cells.i.length); // clean all heights grid.cells.h = new Uint8Array(grid.cells.i.length); // clean all heights
for (const step of steps) { for (const step of steps) {
@ -878,6 +892,7 @@ function editHeightmap() {
else if (type === "Range") addRange(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 === "Trough") addTrough(count, height, x, y);
else if (type === "Strait") addStrait(count, dist); else if (type === "Strait") addStrait(count, dist);
else if (type === "Insulate") insulate(+count);
else if (type === "Add") modify(dist, +count, 1); else if (type === "Add") modify(dist, +count, 1);
else if (type === "Multiply") modify(dist, 0, +count); else if (type === "Multiply") modify(dist, 0, +count);
else if (type === "Smooth") smooth(+count); else if (type === "Smooth") smooth(+count);
@ -887,12 +902,12 @@ function editHeightmap() {
updateStatistics(); updateStatistics();
mockHeightmap(); mockHeightmap();
if (document.getElementById("preview")) drawHeightmapPreview(); // update heightmap preview if opened if (byId("preview")) drawHeightmapPreview(); // update heightmap preview if opened
if (document.getElementById("canvas3d")) ThreeD.redraw(); // update 3d heightmap preview if opened if (byId("canvas3d")) ThreeD.redraw(); // update 3d heightmap preview if opened
} }
function downloadTemplate() { function downloadTemplate() {
const body = document.getElementById("templateBody"); const body = byId("templateBody");
body.dataset.changed = 0; body.dataset.changed = 0;
const steps = body.querySelectorAll("#templateBody > div"); const steps = body.querySelectorAll("#templateBody > div");
if (!steps.length) return; if (!steps.length) return;
@ -977,18 +992,18 @@ function editHeightmap() {
})(); })();
// add listeners // add listeners
document.getElementById("convertImageLoad").addEventListener("click", () => imageToLoad.click()); byId("convertImageLoad").on("click", () => imageToLoad.click());
document.getElementById("imageToLoad").addEventListener("change", loadImage); byId("imageToLoad").on("change", loadImage);
document.getElementById("convertAutoLum").addEventListener("click", () => autoAssing("lum")); byId("convertAutoLum").on("click", () => autoAssing("lum"));
document.getElementById("convertAutoHue").addEventListener("click", () => autoAssing("hue")); byId("convertAutoHue").on("click", () => autoAssing("hue"));
document.getElementById("convertAutoFMG").addEventListener("click", () => autoAssing("scheme")); byId("convertAutoFMG").on("click", () => autoAssing("scheme"));
document.getElementById("convertColorsButton").addEventListener("click", setConvertColorsNumber); byId("convertColorsButton").on("click", setConvertColorsNumber);
document.getElementById("convertComplete").addEventListener("click", applyConversion); byId("convertComplete").on("click", applyConversion);
document.getElementById("convertCancel").addEventListener("click", cancelConversion); byId("convertCancel").on("click", cancelConversion);
document.getElementById("convertOverlay").addEventListener("input", function () { byId("convertOverlay").on("input", function () {
setOverlayOpacity(this.value); setOverlayOpacity(this.value);
}); });
document.getElementById("convertOverlayNumber").addEventListener("input", function () { byId("convertOverlayNumber").on("input", function () {
setOverlayOpacity(this.value); setOverlayOpacity(this.value);
}); });
@ -1012,7 +1027,7 @@ function editHeightmap() {
document.body.appendChild(img); document.body.appendChild(img);
img.onload = function () { img.onload = function () {
const ctx = document.getElementById("canvas").getContext("2d"); const ctx = byId("canvas").getContext("2d");
ctx.drawImage(img, 0, 0, graphWidth, graphHeight); ctx.drawImage(img, 0, 0, graphWidth, graphHeight);
heightsFromImage(+convertColors.value); heightsFromImage(+convertColors.value);
resetZoom(); resetZoom();
@ -1023,7 +1038,7 @@ function editHeightmap() {
} }
function heightsFromImage(count) { function heightsFromImage(count) {
const sourceImage = document.getElementById("canvas"); const sourceImage = byId("canvas");
const sampleCanvas = document.createElement("canvas"); const sampleCanvas = document.createElement("canvas");
sampleCanvas.width = grid.cellsX; sampleCanvas.width = grid.cellsX;
sampleCanvas.height = grid.cellsY; sampleCanvas.height = grid.cellsY;
@ -1062,7 +1077,7 @@ function editHeightmap() {
.attr("class", "color-div") .attr("class", "color-div")
.on("click", colorClicked); .on("click", colorClicked);
document.getElementById("colorsUnassignedNumber").innerHTML = colors.length; byId("colorsUnassignedNumber").innerHTML = colors.length;
} }
function mapClicked() { function mapClicked() {
@ -1119,8 +1134,8 @@ function editHeightmap() {
colorsAssigned.appendChild(selectedColor); colorsAssigned.appendChild(selectedColor);
colorsAssigned.style.display = "block"; colorsAssigned.style.display = "block";
document.getElementById("colorsUnassignedNumber").innerHTML = colorsUnassigned.childElementCount - 2; byId("colorsUnassignedNumber").innerHTML = colorsUnassigned.childElementCount - 2;
document.getElementById("colorsAssignedNumber").innerHTML = colorsAssigned.childElementCount - 2; byId("colorsAssignedNumber").innerHTML = colorsAssigned.childElementCount - 2;
} }
} }
@ -1187,7 +1202,7 @@ function editHeightmap() {
colorsAssigned.style.display = "block"; colorsAssigned.style.display = "block";
colorsUnassigned.style.display = "none"; colorsUnassigned.style.display = "none";
document.getElementById("colorsAssignedNumber").innerHTML = colorsAssigned.childElementCount - 2; byId("colorsAssignedNumber").innerHTML = colorsAssigned.childElementCount - 2;
} }
function setConvertColorsNumber() { function setConvertColorsNumber() {
@ -1203,7 +1218,7 @@ function editHeightmap() {
function setOverlayOpacity(v) { function setOverlayOpacity(v) {
convertOverlay.value = convertOverlayNumber.value = v; convertOverlay.value = convertOverlayNumber.value = v;
document.getElementById("canvas").style.opacity = v; byId("canvas").style.opacity = v;
} }
function applyConversion() { function applyConversion() {
@ -1230,10 +1245,10 @@ function editHeightmap() {
} }
function restoreImageConverterState() { function restoreImageConverterState() {
const canvas = document.getElementById("canvas"); const canvas = byId("canvas");
if (canvas) canvas.remove(); if (canvas) canvas.remove();
const image = document.getElementById("imageToConvert"); const image = byId("imageToConvert");
if (image) image.remove(); if (image) image.remove();
d3.select("#imageConverter").selectAll("div.color-div").remove(); d3.select("#imageConverter").selectAll("div.color-div").remove();
@ -1275,8 +1290,8 @@ function editHeightmap() {
} }
function toggleHeightmapPreview() { function toggleHeightmapPreview() {
if (document.getElementById("preview")) { if (byId("preview")) {
document.getElementById("preview").remove(); byId("preview").remove();
return; return;
} }
const preview = document.createElement("canvas"); const preview = document.createElement("canvas");
@ -1284,13 +1299,13 @@ function editHeightmap() {
preview.width = grid.cellsX; preview.width = grid.cellsX;
preview.height = grid.cellsY; preview.height = grid.cellsY;
document.body.insertBefore(preview, optionsContainer); document.body.insertBefore(preview, optionsContainer);
preview.addEventListener("mouseover", () => tip("Heightmap preview. Click to download a screen-sized image")); preview.on("mouseover", () => tip("Heightmap preview. Click to download a screen-sized image"));
preview.addEventListener("click", downloadPreview); preview.on("click", downloadPreview);
drawHeightmapPreview(); drawHeightmapPreview();
} }
function drawHeightmapPreview() { function drawHeightmapPreview() {
const ctx = document.getElementById("preview").getContext("2d"); const ctx = byId("preview").getContext("2d");
const imageData = ctx.createImageData(grid.cellsX, grid.cellsY); const imageData = ctx.createImageData(grid.cellsX, grid.cellsY);
grid.cells.h.forEach((height, i) => { grid.cells.h.forEach((height, i) => {
@ -1306,7 +1321,7 @@ function editHeightmap() {
} }
function downloadPreview() { function downloadPreview() {
const preview = document.getElementById("preview"); const preview = byId("preview");
const dataURL = preview.toDataURL("image/png"); const dataURL = preview.toDataURL("image/png");
const img = new Image(); const img = new Image();